jev-recipes 0.8.5 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Node.js version](https://img.shields.io/node/v/jev-recipes)](https://nodejs.org/)
8
8
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/agencyenterprise/jev-recipes/blob/main/LICENSE)
9
9
 
10
- [Quickstart](#use-a-recipe) | [Live music app](https://jev-ai-music.com/) | [Coding assistants](https://github.com/agencyenterprise/jev-recipes/blob/main/docs/coding-assistants.md) | [Recipe catalog](https://github.com/agencyenterprise/jev-recipes/blob/main/recipes/README.md) | [API vs. SDK vs. recipes](https://github.com/agencyenterprise/jev-recipes/blob/main/docs/api-sdk-recipes.md) | [Contributing](https://github.com/agencyenterprise/jev-recipes/blob/main/CONTRIBUTING.md)
10
+ [Quickstart](#use-a-recipe) | [Live music app](https://jev-ai-music.com/) | [Coding assistants](https://github.com/agencyenterprise/jev-recipes/blob/main/docs/coding-assistants.md) | [Recipe catalog](https://github.com/agencyenterprise/jev-recipes/blob/main/recipes/README.md) | [API vs. SDK vs. recipes](https://github.com/agencyenterprise/jev-recipes/blob/main/docs/api-sdk-recipes.md) | [Agent frameworks](https://github.com/agencyenterprise/jev-recipes/blob/main/docs/framework-adapters.md) | [Contributing](https://github.com/agencyenterprise/jev-recipes/blob/main/CONTRIBUTING.md)
11
11
 
12
12
  <!-- BEGIN GENERATED: summary -->
13
13
 
@@ -243,6 +243,26 @@ console.log(`${result.requestsMade} calls for ${result.requestCount} tickets`);
243
243
 
244
244
  Accuracy can drop as batches grow. Measure on your own labeled data and tune `batchSize`; the [eval harness](https://github.com/agencyenterprise/jev-recipes/blob/main/CONTRIBUTING.md#model-evaluation) reports calibration per threshold.
245
245
 
246
+ ## Use inside an agent framework
247
+
248
+ `jev-recipes/ai-sdk` and `jev-recipes/langchain` plug the harness recipes into the loop you already run. With the Vercel AI SDK, `guardTools` reviews every tool call with `tool-call-gate`, `routeModelStep` picks the model per call with `model-route`, and `completionCheck` accepts a completion claim only when `completion-gate` sees evidence:
249
+
250
+ ```js
251
+ import { generateText, stepCountIs } from 'ai';
252
+ import { completionCheck, guardTools, routeModelStep } from 'jev-recipes/ai-sdk';
253
+
254
+ const done = completionCheck();
255
+ const result = await generateText({
256
+ model: careful,
257
+ prompt: 'Run the account tests and publish release 1.2.0.',
258
+ tools: { ...guardTools(tools, { policy: 'Ask before publishing.' }), ...done.tools },
259
+ prepareStep: routeModelStep({ candidates: [{ id: 'fast', text: 'Small edits.', model: fast }] }),
260
+ stopWhen: [stepCountIs(8), done.stopWhen],
261
+ });
262
+ ```
263
+
264
+ Any recipe becomes a tool with `recipeTools(['route', 'verify'])`. The LangChain adapter offers the same guard, completion, and recipe tools for `bindTools`, `ToolNode`, and `createAgent`. Install `ai` or `@langchain/core` yourself; both are optional peers. See the [framework adapter guide](https://github.com/agencyenterprise/jev-recipes/blob/main/docs/framework-adapters.md) and the offline [AI SDK](https://github.com/agencyenterprise/jev-recipes/blob/main/examples/ai-sdk-agent/README.md) and [LangChain](https://github.com/agencyenterprise/jev-recipes/blob/main/examples/langchain-tools/README.md) examples.
265
+
246
266
  ## Use a local or alternative decision model
247
267
 
248
268
  Recipes talk to any server that implements the TypeSafe `systemOne` wire format. Point the SDK client at it with `baseURL`, or set `TYPESAFE_BASE_URL` in the environment, and pass the client to any recipe:
@@ -0,0 +1,38 @@
1
+ import type { LanguageModel, PrepareStepFunction, StopCondition, Tool, ToolSet } from 'ai';
2
+ import type { RecipeName } from '../../catalog/schema.js';
3
+ import type { ModelRouteResult } from '../../recipes/model-route/index.js';
4
+ import type { RecipeOptions } from '../../src/schema.js';
5
+ import type { CompletionOptions, CompletionToolOutput, GuardOptions, RecipeToolOptions } from '../shared.js';
6
+ export type { BlockedToolOutput, CompletionEvent, CompletionOptions, CompletionToolOutput, GuardEvent, GuardOptions, RecipeToolOptions, } from '../shared.js';
7
+ export type RecipeTool = Tool<Record<string, unknown>, Record<string, unknown>>;
8
+ export declare function recipeTool(name: RecipeName, options?: RecipeToolOptions): RecipeTool;
9
+ export declare function recipeTools(names: readonly RecipeName[], options?: RecipeToolOptions): Record<string, RecipeTool>;
10
+ export type GuardToolsOptions = GuardOptions & {
11
+ request?: string;
12
+ };
13
+ export declare function guardTools<TOOLS extends ToolSet>(tools: TOOLS, options?: GuardToolsOptions): TOOLS;
14
+ export type ModelCandidate = {
15
+ id: string;
16
+ text: string;
17
+ model: LanguageModel;
18
+ };
19
+ export type RouteModelStepOptions = RecipeOptions & {
20
+ candidates: readonly ModelCandidate[];
21
+ request?: string;
22
+ context?: string;
23
+ minConfidence?: number;
24
+ fallback?: LanguageModel;
25
+ onDecision?: (event: {
26
+ decision: ModelRouteResult;
27
+ model: LanguageModel | undefined;
28
+ }) => void;
29
+ };
30
+ export declare function routeModelStep(options: RouteModelStepOptions): PrepareStepFunction<any, any>;
31
+ export type CompletionCheck = {
32
+ tools: Record<string, Tool<{
33
+ report: string;
34
+ evidence?: string;
35
+ }, CompletionToolOutput>>;
36
+ stopWhen: StopCondition<any, any>;
37
+ };
38
+ export declare function completionCheck(options?: CompletionOptions): CompletionCheck;
@@ -0,0 +1,88 @@
1
+ import { jsonSchema, tool } from 'ai';
2
+ import { modelRoute } from '../../recipes/model-route/index.js';
3
+ import { COMPLETION_TOOL_DESCRIPTION, COMPLETION_TOOL_INPUT_SCHEMA, DEFAULT_COMPLETION_TOOL_NAME, blockedOutput, defineRecipeTool, messageText, requireText, reviewCompletion, reviewToolCall, } from '../shared.js';
4
+ export function recipeTool(name, options = {}) {
5
+ const definition = defineRecipeTool(name, options);
6
+ return tool({
7
+ description: definition.description,
8
+ inputSchema: jsonSchema(definition.inputSchema),
9
+ execute: async (input, executionOptions) => definition.execute(input, executionOptions.abortSignal),
10
+ });
11
+ }
12
+ export function recipeTools(names, options = {}) {
13
+ return Object.fromEntries(names.map((name) => [name, recipeTool(name, options)]));
14
+ }
15
+ export function guardTools(tools, options = {}) {
16
+ const { request, ...guardOptions } = options;
17
+ const guarded = Object.entries(tools).map(([toolName, definition]) => {
18
+ if (!('execute' in definition) || typeof definition.execute !== 'function')
19
+ return [toolName, definition];
20
+ const execute = definition.execute;
21
+ const wrapped = {
22
+ ...definition,
23
+ execute: async (input, executionOptions) => {
24
+ const decision = await reviewToolCall({
25
+ toolName,
26
+ input,
27
+ request: request ?? requireText(lastUserText(executionOptions.messages), 'request'),
28
+ }, guardOptions, executionOptions.abortSignal);
29
+ if (decision.action !== 'allow')
30
+ return blockedOutput(toolName, decision);
31
+ return execute(input, executionOptions);
32
+ },
33
+ };
34
+ return [toolName, wrapped];
35
+ });
36
+ return Object.fromEntries(guarded);
37
+ }
38
+ export function routeModelStep(options) {
39
+ const { candidates, request, context, minConfidence, fallback, onDecision, ...recipeOptions } = options;
40
+ if (!candidates.length)
41
+ throw new Error('Supply at least one model candidate.');
42
+ const decisions = new WeakMap();
43
+ return async ({ initialMessages }) => {
44
+ if (!decisions.has(initialMessages)) {
45
+ const decision = await modelRoute({
46
+ request: request ?? requireText(firstUserText(initialMessages), 'request'),
47
+ models: candidates.map(({ id, text }) => ({ id, text })),
48
+ ...(context === undefined ? {} : { context }),
49
+ ...(minConfidence === undefined ? {} : { minConfidence }),
50
+ }, recipeOptions);
51
+ const selected = decision.status === 'ready' && decision.selection !== null
52
+ ? candidates.find((candidate) => candidate.id === decision.selection)?.model
53
+ : fallback;
54
+ onDecision?.({ decision, model: selected });
55
+ decisions.set(initialMessages, selected);
56
+ }
57
+ const model = decisions.get(initialMessages);
58
+ return model === undefined ? {} : { model };
59
+ };
60
+ }
61
+ export function completionCheck(options = {}) {
62
+ const toolName = options.toolName ?? DEFAULT_COMPLETION_TOOL_NAME;
63
+ const report = tool({
64
+ description: COMPLETION_TOOL_DESCRIPTION,
65
+ inputSchema: jsonSchema(COMPLETION_TOOL_INPUT_SCHEMA),
66
+ execute: async (input, executionOptions) => reviewCompletion({
67
+ task: options.task ?? requireText(firstUserText(executionOptions.messages), 'task'),
68
+ report: input.report,
69
+ evidence: input.evidence,
70
+ }, options, executionOptions.abortSignal),
71
+ });
72
+ return {
73
+ tools: { [toolName]: report },
74
+ stopWhen: ({ steps }) => {
75
+ const last = steps.at(-1);
76
+ return (last?.toolResults.some((result) => result.toolName === toolName &&
77
+ typeof result.output === 'object' &&
78
+ result.output !== null &&
79
+ result.output.accepted === true) ?? false);
80
+ },
81
+ };
82
+ }
83
+ function lastUserText(messages) {
84
+ return messageText(messages, 'user', 'last');
85
+ }
86
+ function firstUserText(messages) {
87
+ return messageText(messages, 'user', 'first');
88
+ }
@@ -0,0 +1,24 @@
1
+ import type { DynamicStructuredTool } from '@langchain/core/tools';
2
+ export type GuardableTool = {
3
+ name: string;
4
+ description: string;
5
+ schema: unknown;
6
+ returnDirect?: boolean;
7
+ invoke(input: any, config?: any): Promise<unknown>;
8
+ };
9
+ import type { RecipeName } from '../../catalog/schema.js';
10
+ import { COMPLETION_TOOL_INPUT_SCHEMA } from '../shared.js';
11
+ import type { CompletionOptions, GuardOptions, RecipeToolOptions } from '../shared.js';
12
+ export type { BlockedToolOutput, CompletionEvent, CompletionOptions, CompletionToolOutput, GuardEvent, GuardOptions, RecipeToolOptions, } from '../shared.js';
13
+ type JsonSchema = Record<string, unknown>;
14
+ export type RecipeTool = DynamicStructuredTool<JsonSchema>;
15
+ export declare function recipeTool(name: RecipeName, options?: RecipeToolOptions): RecipeTool;
16
+ export declare function recipeTools(names: readonly RecipeName[], options?: RecipeToolOptions): RecipeTool[];
17
+ export type GuardRequest = string | ((input: unknown, config: unknown) => string);
18
+ export type GuardToolsOptions = GuardOptions & {
19
+ request: GuardRequest;
20
+ };
21
+ export declare function guardTools(tools: readonly GuardableTool[], options: GuardToolsOptions): DynamicStructuredTool[];
22
+ export declare function completionTool(options: CompletionOptions & {
23
+ task: string;
24
+ }): DynamicStructuredTool<typeof COMPLETION_TOOL_INPUT_SCHEMA>;
@@ -0,0 +1,42 @@
1
+ import { tool } from '@langchain/core/tools';
2
+ import { COMPLETION_TOOL_DESCRIPTION, COMPLETION_TOOL_INPUT_SCHEMA, DEFAULT_COMPLETION_TOOL_NAME, blockedOutput, defineRecipeTool, requireText, reviewCompletion, reviewToolCall, } from '../shared.js';
3
+ export function recipeTool(name, options = {}) {
4
+ const definition = defineRecipeTool(name, options);
5
+ return tool((input, config) => definition.execute(input, config?.signal), {
6
+ name: definition.name,
7
+ description: definition.description,
8
+ schema: definition.inputSchema,
9
+ });
10
+ }
11
+ export function recipeTools(names, options = {}) {
12
+ return names.map((name) => recipeTool(name, options));
13
+ }
14
+ export function guardTools(tools, options) {
15
+ const { request, ...guardOptions } = options;
16
+ return tools.map((original) => {
17
+ const schema = original.schema;
18
+ return tool(async (input, config) => {
19
+ const decision = await reviewToolCall({
20
+ toolName: original.name,
21
+ input,
22
+ request: typeof request === 'function' ? request(input, config) : request,
23
+ }, guardOptions, config?.signal);
24
+ if (decision.action !== 'allow')
25
+ return blockedOutput(original.name, decision);
26
+ const { toolCall: _toolCall, ...delegatedConfig } = config ?? {};
27
+ return original.invoke(input, delegatedConfig);
28
+ }, {
29
+ name: original.name,
30
+ description: original.description,
31
+ schema,
32
+ ...(original.returnDirect === undefined ? {} : { returnDirect: original.returnDirect }),
33
+ });
34
+ });
35
+ }
36
+ export function completionTool(options) {
37
+ return tool((input, config) => reviewCompletion({ task: requireText(options.task, 'task'), report: input.report, evidence: input.evidence }, options, config?.signal), {
38
+ name: options.toolName ?? DEFAULT_COMPLETION_TOOL_NAME,
39
+ description: COMPLETION_TOOL_DESCRIPTION,
40
+ schema: COMPLETION_TOOL_INPUT_SCHEMA,
41
+ });
42
+ }
@@ -0,0 +1,88 @@
1
+ import type { RecipeName } from '../catalog/schema.js';
2
+ import type { CompletionGateResult } from '../recipes/completion-gate/index.js';
3
+ import type { ToolCallGateResult } from '../recipes/tool-call-gate/index.js';
4
+ import type { RecipeOptions } from '../src/schema.js';
5
+ export type RecipeToolOptions = RecipeOptions & {
6
+ description?: string;
7
+ };
8
+ export type RecipeToolDefinition = {
9
+ name: RecipeName;
10
+ description: string;
11
+ inputSchema: Record<string, unknown>;
12
+ execute: (input: unknown, signal?: AbortSignal) => Promise<Record<string, unknown>>;
13
+ };
14
+ export type GuardOptions = RecipeOptions & {
15
+ policy?: string;
16
+ context?: string;
17
+ minConfidence?: number;
18
+ onDecision?: (event: GuardEvent) => void;
19
+ };
20
+ export type GuardEvent = {
21
+ toolName: string;
22
+ input: unknown;
23
+ decision: ToolCallGateResult;
24
+ };
25
+ export type BlockedToolOutput = {
26
+ blocked: true;
27
+ action: 'ask' | 'deny';
28
+ verdict: ToolCallGateResult['verdict'];
29
+ status: ToolCallGateResult['status'];
30
+ detected: ToolCallGateResult['detected'];
31
+ confidence: number;
32
+ message: string;
33
+ };
34
+ export type CompletionOptions = RecipeOptions & {
35
+ task?: string;
36
+ evidence?: string;
37
+ toolName?: string;
38
+ minConfidence?: number;
39
+ onDecision?: (event: CompletionEvent) => void;
40
+ };
41
+ export type CompletionEvent = {
42
+ report: string;
43
+ evidence: string | undefined;
44
+ decision: CompletionGateResult;
45
+ };
46
+ export type CompletionToolOutput = {
47
+ accepted: boolean;
48
+ verdict: CompletionGateResult['verdict'];
49
+ status: CompletionGateResult['status'];
50
+ detected: CompletionGateResult['detected'];
51
+ confidence: number;
52
+ message: string;
53
+ };
54
+ export declare const DEFAULT_COMPLETION_TOOL_NAME = "report_completion";
55
+ export declare const COMPLETION_TOOL_DESCRIPTION = "Report that the task is complete. State what was done in report, and put the evidence that demonstrates it, such as test output, command results, or observed behavior, in evidence. A claim without evidence is not accepted.";
56
+ export declare const COMPLETION_TOOL_INPUT_SCHEMA: {
57
+ readonly type: "object";
58
+ readonly properties: {
59
+ readonly report: {
60
+ readonly type: "string";
61
+ readonly description: "What was done and how the task was completed.";
62
+ };
63
+ readonly evidence: {
64
+ readonly type: "string";
65
+ readonly description: "Concrete evidence that demonstrates completion, such as test output.";
66
+ };
67
+ };
68
+ readonly required: readonly ["report"];
69
+ readonly additionalProperties: false;
70
+ };
71
+ export declare function defineRecipeTool(name: RecipeName, options?: RecipeToolOptions): RecipeToolDefinition;
72
+ export declare function renderToolCall(toolName: string, input: unknown): string;
73
+ export declare function reviewToolCall(call: {
74
+ toolName: string;
75
+ input: unknown;
76
+ request: string;
77
+ }, options: GuardOptions, signal?: AbortSignal): Promise<ToolCallGateResult>;
78
+ export declare function blockedOutput(toolName: string, decision: ToolCallGateResult): BlockedToolOutput;
79
+ export declare function reviewCompletion(claim: {
80
+ task: string;
81
+ report: string;
82
+ evidence: string | undefined;
83
+ }, options: CompletionOptions, signal?: AbortSignal): Promise<CompletionToolOutput>;
84
+ export declare function messageText(messages: readonly {
85
+ role: string;
86
+ content: unknown;
87
+ }[], role: 'user' | 'assistant', position: 'first' | 'last'): string | undefined;
88
+ export declare function requireText(value: string | undefined, description: string): string;
@@ -0,0 +1,132 @@
1
+ import { describeRecipe } from '../catalog/index.js';
2
+ import { loadRecipe } from '../catalog/runner.js';
3
+ import { completionGate } from '../recipes/completion-gate/index.js';
4
+ import { toolCallGate } from '../recipes/tool-call-gate/index.js';
5
+ export const DEFAULT_COMPLETION_TOOL_NAME = 'report_completion';
6
+ export const COMPLETION_TOOL_DESCRIPTION = 'Report that the task is complete. State what was done in report, and put the evidence that demonstrates it, such as test output, command results, or observed behavior, in evidence. A claim without evidence is not accepted.';
7
+ export const COMPLETION_TOOL_INPUT_SCHEMA = {
8
+ type: 'object',
9
+ properties: {
10
+ report: { type: 'string', description: 'What was done and how the task was completed.' },
11
+ evidence: {
12
+ type: 'string',
13
+ description: 'Concrete evidence that demonstrates completion, such as test output.',
14
+ },
15
+ },
16
+ required: ['report'],
17
+ additionalProperties: false,
18
+ };
19
+ export function defineRecipeTool(name, options = {}) {
20
+ const { description, ...recipeOptions } = options;
21
+ const recipe = describeRecipe(name);
22
+ const summary = description ??
23
+ (recipe.useWhen ? `${recipe.description} Use when: ${recipe.useWhen}` : recipe.description);
24
+ return {
25
+ name: recipe.id,
26
+ description: summary,
27
+ inputSchema: recipe.inputSchema,
28
+ execute: async (input, signal) => {
29
+ const run = await loadRecipe(name);
30
+ const result = await run(input, withSignal(recipeOptions, signal));
31
+ return result;
32
+ },
33
+ };
34
+ }
35
+ export function renderToolCall(toolName, input) {
36
+ return input === undefined ? `${toolName}()` : `${toolName}(${JSON.stringify(input)})`;
37
+ }
38
+ export async function reviewToolCall(call, options, signal) {
39
+ const { policy, context, minConfidence, onDecision, ...recipeOptions } = options;
40
+ const decision = await toolCallGate({
41
+ request: call.request,
42
+ toolCall: renderToolCall(call.toolName, call.input),
43
+ ...(context === undefined ? {} : { context }),
44
+ ...(policy === undefined ? {} : { policy }),
45
+ ...(minConfidence === undefined ? {} : { minConfidence }),
46
+ }, withSignal(recipeOptions, signal));
47
+ onDecision?.({ toolName: call.toolName, input: call.input, decision });
48
+ return decision;
49
+ }
50
+ export function blockedOutput(toolName, decision) {
51
+ const action = decision.action === 'deny' ? 'deny' : 'ask';
52
+ const risks = decision.detected.length ? ` Detected risks: ${decision.detected.join(', ')}.` : '';
53
+ const message = action === 'deny'
54
+ ? `The call to ${toolName} was refused by policy and did not run.${risks} Do not retry it. Explain what you intended and continue with permitted work.`
55
+ : `The call to ${toolName} did not run because a person must confirm it first.${risks} Do not retry it unattended. Explain what you intended and continue with other permitted work.`;
56
+ return {
57
+ blocked: true,
58
+ action,
59
+ verdict: decision.verdict,
60
+ status: decision.status,
61
+ detected: decision.detected,
62
+ confidence: decision.confidence,
63
+ message,
64
+ };
65
+ }
66
+ export async function reviewCompletion(claim, options, signal) {
67
+ const { evidence: fixedEvidence, minConfidence, onDecision, ...rest } = options;
68
+ const { task: _task, toolName: _toolName, ...recipeOptions } = rest;
69
+ const evidence = [fixedEvidence, claim.evidence]
70
+ .filter((text) => Boolean(text?.trim()))
71
+ .join('\n\n');
72
+ const decision = await completionGate({
73
+ task: claim.task,
74
+ report: claim.report,
75
+ ...(evidence ? { evidence } : {}),
76
+ ...(minConfidence === undefined ? {} : { minConfidence }),
77
+ }, withSignal(recipeOptions, signal));
78
+ onDecision?.({ report: claim.report, evidence: evidence || undefined, decision });
79
+ const accepted = decision.status === 'ready' && decision.verdict === 'complete';
80
+ const signals = decision.detected.length ? ` Signals: ${decision.detected.join(', ')}.` : '';
81
+ const message = accepted
82
+ ? 'The completion report was accepted.'
83
+ : decision.status === 'review'
84
+ ? `The completion report could not be judged confidently (verdict: ${decision.verdict}).${signals} Supply clearer evidence or continue working.`
85
+ : `The completion report was not accepted (verdict: ${decision.verdict}).${signals} Finish the remaining work or supply evidence that demonstrates completion.`;
86
+ return {
87
+ accepted,
88
+ verdict: decision.verdict,
89
+ status: decision.status,
90
+ detected: decision.detected,
91
+ confidence: decision.confidence,
92
+ message,
93
+ };
94
+ }
95
+ export function messageText(messages, role, position) {
96
+ const ordered = position === 'first' ? messages : [...messages].reverse();
97
+ for (const message of ordered) {
98
+ if (message.role !== role)
99
+ continue;
100
+ const text = contentText(message.content);
101
+ if (text)
102
+ return text;
103
+ }
104
+ return undefined;
105
+ }
106
+ function contentText(content) {
107
+ if (typeof content === 'string')
108
+ return content.trim();
109
+ if (!Array.isArray(content))
110
+ return '';
111
+ return content
112
+ .map((part) => typeof part === 'object' && part !== null && 'text' in part && typeof part.text === 'string'
113
+ ? part.text
114
+ : '')
115
+ .filter((text) => text.trim())
116
+ .join('\n')
117
+ .trim();
118
+ }
119
+ export function requireText(value, description) {
120
+ if (!value)
121
+ throw new Error(`Cannot derive ${description}; supply it explicitly.`);
122
+ return value;
123
+ }
124
+ function withSignal(options, signal) {
125
+ const { client, model } = options;
126
+ const chosen = options.signal ?? signal;
127
+ return {
128
+ ...(client === undefined ? {} : { client }),
129
+ ...(model === undefined ? {} : { model }),
130
+ ...(chosen === undefined ? {} : { signal: chosen }),
131
+ };
132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jev-recipes",
3
- "version": "0.8.5",
3
+ "version": "0.9.0",
4
4
  "description": "Small, composable Jev decisions for retrieval, conversations, and agent workflows.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,6 +27,14 @@
27
27
  "types": "./dist/evaluation/index.d.ts",
28
28
  "import": "./dist/evaluation/index.js"
29
29
  },
30
+ "./ai-sdk": {
31
+ "types": "./dist/adapters/ai-sdk/index.d.ts",
32
+ "import": "./dist/adapters/ai-sdk/index.js"
33
+ },
34
+ "./langchain": {
35
+ "types": "./dist/adapters/langchain/index.d.ts",
36
+ "import": "./dist/adapters/langchain/index.js"
37
+ },
30
38
  "./package.json": "./package.json",
31
39
  "./action-compare": {
32
40
  "types": "./dist/recipes/action-compare/index.d.ts",
@@ -1035,6 +1043,8 @@
1035
1043
  "dist/cli/*.js",
1036
1044
  "dist/evaluation/**/*.js",
1037
1045
  "dist/evaluation/**/*.d.ts",
1046
+ "dist/adapters/**/*.js",
1047
+ "dist/adapters/**/*.d.ts",
1038
1048
  "dist/cli/*.d.ts",
1039
1049
  "dist/recipes/*/demo.json",
1040
1050
  "README.md",
@@ -1067,15 +1077,29 @@
1067
1077
  "test:coverage": "vitest run --coverage",
1068
1078
  "eval:featured": "node --env-file-if-exists=.env evals/featured.mjs",
1069
1079
  "site:build": "npm run generate:check && npm run build && node site/build.mjs",
1070
- "site:preview": "node site/preview.mjs"
1080
+ "site:preview": "node --env-file-if-exists=.env site/preview.mjs"
1071
1081
  },
1072
1082
  "dependencies": {
1073
1083
  "@typesafe-ai/sdk": "^0.6.0",
1074
1084
  "zod": "^4.6.5"
1075
1085
  },
1086
+ "peerDependencies": {
1087
+ "ai": ">=7.0.0",
1088
+ "@langchain/core": ">=1.0.0"
1089
+ },
1090
+ "peerDependenciesMeta": {
1091
+ "ai": {
1092
+ "optional": true
1093
+ },
1094
+ "@langchain/core": {
1095
+ "optional": true
1096
+ }
1097
+ },
1076
1098
  "devDependencies": {
1099
+ "@langchain/core": "^1.2.13",
1077
1100
  "@types/node": "^22.0.0",
1078
1101
  "@vitest/coverage-v8": "4.1.11",
1102
+ "ai": "^7.0.122",
1079
1103
  "prettier": "3.9.8",
1080
1104
  "rimraf": "6.1.3",
1081
1105
  "typescript": "~5.9.3",