Skip to content

Commit 9408df6

Browse files
committed
fix(jev): preserve Agent tool criteria and rubric schema
1 parent 52e12cf commit 9408df6

6 files changed

Lines changed: 146 additions & 10 deletions

File tree

‎apps/docs/content/docs/integrations/jev.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
1212

1313
## Usage Instructions
1414

15-
Use TypeSafe AI’s Jev to classify content, score it against a rubric, or evaluate yes/no questions. Evaluate multiple questions against shared state in one request and route results using probabilities and confidence. Requires your own TypeSafe API key.
15+
Use TypeSafe AI’s Jev to classify content, score it against a rubric, or evaluate yes/no questions. Add Jev to an Agent’s tools to make decisions during a conversation, or use the Jev block for an explicit workflow step. Evaluate multiple questions against shared state in one request and route results using probabilities and confidence. Requires your own TypeSafe API key.
1616

1717

1818

@@ -56,7 +56,7 @@ Score content against an ordered rubric with Jev, including probabilities and co
5656
| `model` | string | No | Model ID or alias: jev-1.13.0, jev-latest, or jev-preview. Defaults to jev-1.13.0. |
5757
| `state` | json | Yes | Content to evaluate: a text string, JSON object, or array. |
5858
| `instructions` | json | Yes | The question to evaluate, as text, a JSON object, or an array. |
59-
| `criteria` | json | Yes | Ordered array of 2–10 rubric descriptions, from lowest to highest score. |
59+
| `criteria` | array | Yes | Ordered array of 2–10 rubric descriptions, from lowest to highest score. |
6060

6161
#### Output
6262

‎apps/sim/blocks/blocks/jev.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const JevBlock: BlockConfig<
1414
name: 'Jev',
1515
description: 'Make structured decisions with TypeSafe AI',
1616
longDescription:
17-
'Use TypeSafe AI’s Jev to classify content, score it against a rubric, or evaluate yes/no questions. Evaluate multiple questions against shared state in one request and route results using probabilities and confidence. Requires your own TypeSafe API key.',
17+
'Use TypeSafe AI’s Jev to classify content, score it against a rubric, or evaluate yes/no questions. Add Jev to an Agent’s tools to make decisions during a conversation, or use the Jev block for an explicit workflow step. Evaluate multiple questions against shared state in one request and route results using probabilities and confidence. Requires your own TypeSafe API key.',
1818
docsLink: 'https://docs.sim.ai/integrations/jev',
1919
category: 'tools',
2020
integrationType: IntegrationType.AI,
@@ -152,11 +152,11 @@ export const JevBlock: BlockConfig<
152152
params: (params) => {
153153
switch (params.operation || 'jev_choice') {
154154
case 'jev_choice':
155-
return { criteria: params.choiceCriteria }
155+
return { criteria: params.criteria ?? params.choiceCriteria }
156156
case 'jev_score':
157-
return { criteria: params.scoreCriteria }
157+
return { criteria: params.criteria ?? params.scoreCriteria }
158158
case 'jev_noul':
159-
return { criteria: params.noulCriteria }
159+
return { criteria: params.criteria ?? params.noulCriteria }
160160
default:
161161
return {}
162162
}

‎apps/sim/tools/generated/tool-metadata.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { describe, expect, it, vi } from 'vitest'
2+
import { isAgentToolBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/utils'
3+
import { JevBlock } from '@/blocks/blocks/jev'
4+
import { prepareToolExecution, transformBlockTool } from '@/providers/utils'
5+
import { jevChoiceTool, jevEvaluateTool, jevNoulTool, jevScoreTool } from '@/tools/jev'
6+
import { getSubBlocksForToolInput } from '@/tools/params'
7+
import { prepareToolRequest } from '@/tools/request-transport'
8+
import type { ToolConfig } from '@/tools/types'
9+
10+
vi.unmock('@/tools/metadata')
11+
12+
const TOOLS: Record<string, ToolConfig> = {
13+
jev_choice: jevChoiceTool,
14+
jev_score: jevScoreTool,
15+
jev_noul: jevNoulTool,
16+
jev_evaluate: jevEvaluateTool,
17+
}
18+
19+
const CASES = [
20+
{
21+
operation: 'jev_choice',
22+
args: { criteria: { billing: 'Payments', technical: 'Bugs' } },
23+
questions: {
24+
result: {
25+
type: 'choice',
26+
instructions: 'Question',
27+
criteria: { billing: 'Payments', technical: 'Bugs' },
28+
},
29+
},
30+
},
31+
{
32+
operation: 'jev_score',
33+
args: { criteria: ['Low', 'High'] },
34+
questions: {
35+
result: { type: 'score', instructions: 'Question', criteria: ['Low', 'High'] },
36+
},
37+
},
38+
{
39+
operation: 'jev_noul',
40+
args: { criteria: { true: 'Urgent', false: 'Routine' } },
41+
questions: {
42+
result: {
43+
type: 'noul',
44+
instructions: 'Question',
45+
criteria: { true: 'Urgent', false: 'Routine' },
46+
},
47+
},
48+
},
49+
{
50+
operation: 'jev_evaluate',
51+
args: { questions: { urgent: { type: 'noul', instructions: 'Is this urgent?' } } },
52+
questions: { urgent: { type: 'noul', instructions: 'Is this urgent?' } },
53+
},
54+
]
55+
56+
async function agentTool(operation: string, params: Record<string, unknown>) {
57+
const tool = await transformBlockTool(
58+
{ type: 'jev', operation, params },
59+
{
60+
selectedOperation: operation,
61+
getAllBlocks: () => [JevBlock],
62+
getTool: (id) => TOOLS[id],
63+
}
64+
)
65+
if (!tool) throw new Error(`Missing Agent tool for ${operation}`)
66+
return tool
67+
}
68+
69+
describe('Jev Agent tools', () => {
70+
it.each(CASES)('offers editable inputs for $operation in the Agent picker', ({ operation }) => {
71+
expect(isAgentToolBlock(JevBlock)).toBe(true)
72+
const fields = getSubBlocksForToolInput(
73+
operation,
74+
'jev',
75+
{ operation },
76+
undefined,
77+
JevBlock
78+
)?.subBlocks
79+
const input = operation === 'jev_evaluate' ? 'questions' : 'criteria'
80+
expect(fields?.find((field) => field.id === input)?.paramVisibility).toBe('user-or-llm')
81+
expect(fields?.find((field) => field.id === 'apiKey')?.paramVisibility).toBe('user-only')
82+
})
83+
84+
describe.each(['model', 'user'] as const)('%s-supplied decision inputs', (source) => {
85+
it.each(CASES)(
86+
'prepares $operation through the shared Agent adapter',
87+
async ({ operation, args, questions }) => {
88+
const userParams: Record<string, unknown> = {
89+
apiKey: 'test-key',
90+
state: '42',
91+
instructions: 'Question',
92+
}
93+
if (source === 'user') {
94+
for (const [key, value] of Object.entries(args)) {
95+
userParams[key] = JSON.stringify(value)
96+
}
97+
}
98+
const tool = await agentTool(operation, userParams)
99+
expect(tool.id).toBe(operation)
100+
expect(tool.parameters.properties).not.toHaveProperty('apiKey')
101+
expect(tool.modelBlockedParams).toContain('apiKey')
102+
const { toolParams } = prepareToolExecution(
103+
tool,
104+
{ ...(source === 'model' ? args : {}), apiKey: 'model-key' },
105+
{},
106+
'test-call'
107+
)
108+
const request = prepareToolRequest(TOOLS[operation], toolParams)
109+
expect(request.headers.get('Authorization')).toBe('Bearer test-key')
110+
expect(JSON.parse(request.body!)).toEqual({
111+
state: '42',
112+
model: 'jev-1.13.0',
113+
questions,
114+
})
115+
}
116+
)
117+
})
118+
119+
it('advertises the Score rubric as a bounded array to the Agent model', async () => {
120+
const tool = await agentTool('jev_score', { apiKey: 'test-key' })
121+
expect(tool.parameters.properties.criteria).toMatchObject({
122+
type: 'array',
123+
minItems: 2,
124+
maxItems: 10,
125+
})
126+
})
127+
})

‎apps/sim/tools/jev/score.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,19 @@ export const jevScoreTool: ToolConfig<JevScoreParams, JevScoreResponse> = {
1919
...JEV_COMMON_PARAMS,
2020
instructions: JEV_INSTRUCTIONS_PARAM,
2121
criteria: {
22-
type: 'json',
22+
type: 'array',
2323
required: true,
2424
visibility: 'user-or-llm',
2525
description: 'Ordered array of 2–10 rubric descriptions, from lowest to highest score.',
26+
minItems: 2,
27+
maxItems: 10,
28+
items: {
29+
anyOf: [
30+
{ type: 'string' },
31+
{ type: 'object', additionalProperties: true },
32+
{ type: 'array', items: {} },
33+
],
34+
},
2635
},
2736
},
2837
request: {

‎packages/deployment-config/src/integrations.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"updatedAt": "2026-09-21",
2+
"updatedAt": "2026-09-22",
33
"integrations": [
44
{
55
"type": "onepassword",
@@ -12564,7 +12564,7 @@
1256412564
"slug": "jev",
1256512565
"name": "Jev",
1256612566
"description": "Make structured decisions with TypeSafe AI",
12567-
"longDescription": "Use TypeSafe AI’s Jev to classify content, score it against a rubric, or evaluate yes/no questions. Evaluate multiple questions against shared state in one request and route results using probabilities and confidence. Requires your own TypeSafe API key.",
12567+
"longDescription": "Use TypeSafe AI’s Jev to classify content, score it against a rubric, or evaluate yes/no questions. Add Jev to an Agent’s tools to make decisions during a conversation, or use the Jev block for an explicit workflow step. Evaluate multiple questions against shared state in one request and route results using probabilities and confidence. Requires your own TypeSafe API key.",
1256812568
"bgColor": "#F386A1",
1256912569
"iconName": "TypeSafeIcon",
1257012570
"docsUrl": "https://docs.sim.ai/integrations/jev",

0 commit comments

Comments
 (0)