@dxos/assistant 0.8.4-main.e098934 → 0.8.4-main.ead640a
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/dist/lib/browser/{chunk-3K5SRE5A.mjs → chunk-CWGVG235.mjs} +59 -83
- package/dist/lib/browser/chunk-CWGVG235.mjs.map +7 -0
- package/dist/lib/browser/extraction/index.mjs +17 -13
- package/dist/lib/browser/extraction/index.mjs.map +4 -4
- package/dist/lib/browser/index.mjs +550 -86
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/{chunk-W7AIB6DP.mjs → chunk-OV5PMQPB.mjs} +59 -83
- package/dist/lib/node-esm/chunk-OV5PMQPB.mjs.map +7 -0
- package/dist/lib/node-esm/extraction/index.mjs +17 -13
- package/dist/lib/node-esm/extraction/index.mjs.map +4 -4
- package/dist/lib/node-esm/index.mjs +550 -86
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/conversation/context.d.ts +14 -7
- package/dist/types/src/conversation/context.d.ts.map +1 -1
- package/dist/types/src/conversation/conversation.d.ts +9 -10
- package/dist/types/src/conversation/conversation.d.ts.map +1 -1
- package/dist/types/src/errors.d.ts +8 -8
- package/dist/types/src/execution-graph.d.ts +82 -0
- package/dist/types/src/execution-graph.d.ts.map +1 -0
- package/dist/types/src/execution-graph.test.d.ts +2 -0
- package/dist/types/src/execution-graph.test.d.ts.map +1 -0
- package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +61 -57
- package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +1 -1
- package/dist/types/src/extraction/extraction.d.ts +10 -6
- package/dist/types/src/extraction/extraction.d.ts.map +1 -1
- package/dist/types/src/extraction/quotes.d.ts +1 -1
- package/dist/types/src/extraction/quotes.d.ts.map +1 -1
- package/dist/types/src/functions/services.d.ts +17 -5
- package/dist/types/src/functions/services.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/session/artifact-diff.d.ts +2 -2
- package/dist/types/src/session/artifact-diff.d.ts.map +1 -1
- package/dist/types/src/session/error-handling.d.ts +2 -2
- package/dist/types/src/session/error-handling.d.ts.map +1 -1
- package/dist/types/src/session/format.d.ts +33 -31
- package/dist/types/src/session/format.d.ts.map +1 -1
- package/dist/types/src/session/observer.d.ts +1 -1
- package/dist/types/src/session/observer.d.ts.map +1 -1
- package/dist/types/src/session/session.d.ts +13 -9
- package/dist/types/src/session/session.d.ts.map +1 -1
- package/dist/types/src/session/toolkit.d.ts +7 -6
- package/dist/types/src/session/toolkit.d.ts.map +1 -1
- package/dist/types/src/session/version-pin.d.ts +2 -2
- package/dist/types/src/session/version-pin.d.ts.map +1 -1
- package/dist/types/src/{types.d.ts → types/artifact.d.ts} +3 -3
- package/dist/types/src/types/artifact.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +2 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +26 -31
- package/src/conversation/context.ts +49 -17
- package/src/conversation/conversation.ts +23 -18
- package/src/execution-graph.test.ts +738 -0
- package/src/execution-graph.ts +500 -0
- package/src/extraction/extraction-llm-function/extraction-llm-function.ts +3 -1
- package/src/extraction/extraction-ner-function/extraction-ner-function.ts +3 -1
- package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +2 -1
- package/src/extraction/extraction.ts +2 -2
- package/src/extraction/quotes.ts +2 -2
- package/src/functions/services.ts +85 -46
- package/src/index.ts +2 -1
- package/src/session/artifact-diff.ts +2 -2
- package/src/session/error-handling.ts +6 -4
- package/src/session/format.test.ts +1 -1
- package/src/session/format.ts +8 -5
- package/src/session/observer.ts +2 -1
- package/src/session/session.conversations.json +1 -0
- package/src/session/session.test.ts +50 -209
- package/src/session/session.ts +31 -20
- package/src/session/toolkit.ts +19 -13
- package/src/session/version-pin.ts +2 -2
- package/src/summarization/summarization.test.ts +8 -3
- package/src/templates/system.tpl +10 -12
- package/src/{types.ts → types/artifact.ts} +2 -2
- package/src/types/index.ts +5 -0
- package/dist/lib/browser/chunk-3K5SRE5A.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-W7AIB6DP.mjs.map +0 -7
- package/dist/types/src/session/version-pin.test.d.ts +0 -2
- package/dist/types/src/session/version-pin.test.d.ts.map +0 -1
- package/dist/types/src/types.d.ts.map +0 -1
- package/src/session/__snapshots__/session.test.ts.snap +0 -32
- package/src/session/version-pin.test.ts +0 -7
|
@@ -2,97 +2,132 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import * as Tool from '@effect/ai/Tool';
|
|
6
|
+
import type * as Toolkit from '@effect/ai/Toolkit';
|
|
7
|
+
import * as Context from 'effect/Context';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
import * as Layer from 'effect/Layer';
|
|
10
|
+
import * as Record from 'effect/Record';
|
|
11
|
+
import * as Schema from 'effect/Schema';
|
|
7
12
|
|
|
8
13
|
import { AiToolNotFoundError, ToolExecutionService, ToolResolverService } from '@dxos/ai';
|
|
9
14
|
import { todo } from '@dxos/debug';
|
|
10
|
-
import {
|
|
15
|
+
import { Query } from '@dxos/echo';
|
|
16
|
+
import { DatabaseService, FunctionDefinition, FunctionInvocationService, FunctionType } from '@dxos/functions';
|
|
11
17
|
import { invariant } from '@dxos/invariant';
|
|
12
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Constructs a `ToolResolverService` whose `resolve(id)` looks up tools in the following order:
|
|
21
|
+
* 1. Toolkit: return an existing tool from the provided `toolkit` if one is present under `id`.
|
|
22
|
+
* 2. Functions in DB: query `FunctionType` by `key=id`; if found, deserialize and project to a tool
|
|
23
|
+
* (propagating any `deployedFunctionId` discovered in the object's metadata).
|
|
24
|
+
* 3. Functions passed in: fall back to a matching `FunctionDefinition` from the `functions` array.
|
|
25
|
+
*
|
|
26
|
+
* If none of the above yield a match, the effect fails with `AiToolNotFoundError`.
|
|
27
|
+
*
|
|
28
|
+
* Requires `DatabaseService` in the environment.
|
|
29
|
+
*/
|
|
13
30
|
export const makeToolResolverFromFunctions = (
|
|
14
31
|
functions: FunctionDefinition<any, any>[],
|
|
15
|
-
toolkit:
|
|
16
|
-
): Layer.Layer<ToolResolverService> => {
|
|
17
|
-
return Layer.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
toolkit: Toolkit.Toolkit<any>,
|
|
33
|
+
): Layer.Layer<ToolResolverService, never, DatabaseService> => {
|
|
34
|
+
return Layer.effect(
|
|
35
|
+
ToolResolverService,
|
|
36
|
+
Effect.gen(function* () {
|
|
37
|
+
const dbService = yield* DatabaseService;
|
|
38
|
+
return {
|
|
39
|
+
resolve: (id): Effect.Effect<Tool.Any, AiToolNotFoundError> =>
|
|
40
|
+
Effect.gen(function* () {
|
|
41
|
+
const tool = toolkit.tools[id];
|
|
42
|
+
if (tool) {
|
|
43
|
+
return tool;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const {
|
|
47
|
+
objects: [dbFunction],
|
|
48
|
+
} = yield* DatabaseService.runQuery(Query.type(FunctionType, { key: id }));
|
|
49
|
+
|
|
50
|
+
const functionDef = dbFunction
|
|
51
|
+
? FunctionDefinition.deserialize(dbFunction)
|
|
52
|
+
: functions.find((fn) => fn.key === id);
|
|
53
|
+
|
|
54
|
+
if (!functionDef) {
|
|
55
|
+
return yield* Effect.fail(new AiToolNotFoundError(id));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return projectFunctionToTool(functionDef);
|
|
59
|
+
}).pipe(Effect.provideService(DatabaseService, dbService)),
|
|
60
|
+
} satisfies Context.Tag.Service<ToolResolverService>;
|
|
30
61
|
}),
|
|
31
|
-
|
|
62
|
+
);
|
|
32
63
|
};
|
|
33
64
|
|
|
34
65
|
export const makeToolExecutionServiceFromFunctions = (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
): Layer.Layer<ToolExecutionService, never, LocalFunctionExecutionService> => {
|
|
66
|
+
toolkit: Toolkit.Toolkit<any>,
|
|
67
|
+
handlersLayer: Layer.Layer<Tool.Handler<any>, never, never>,
|
|
68
|
+
): Layer.Layer<ToolExecutionService, never, FunctionInvocationService> => {
|
|
39
69
|
return Layer.effect(
|
|
40
70
|
ToolExecutionService,
|
|
41
71
|
Effect.gen(function* () {
|
|
42
72
|
const toolkitHandler = yield* toolkit.pipe(Effect.provide(handlersLayer));
|
|
43
|
-
|
|
73
|
+
invariant(isHandlerLike(toolkitHandler));
|
|
74
|
+
const functionInvocationService = yield* FunctionInvocationService;
|
|
44
75
|
|
|
45
76
|
return {
|
|
46
77
|
handlersFor: (toolkit) => {
|
|
47
|
-
const makeHandler = (tool:
|
|
48
|
-
return Effect.fn(
|
|
49
|
-
if (toolkitHandler.tools
|
|
78
|
+
const makeHandler = (tool: Tool.Any): ((params: unknown) => Effect.Effect<unknown, any, any>) => {
|
|
79
|
+
return Effect.fn(`toolFunctionHandler ${tool.name}`)(function* (input: any) {
|
|
80
|
+
if (toolkitHandler.tools[tool.name]) {
|
|
81
|
+
if (Tool.isProviderDefined(tool)) {
|
|
82
|
+
throw new Error('Attempted to call a provider-defined tool');
|
|
83
|
+
}
|
|
50
84
|
// TODO(wittjosiah): Everything is `never` here.
|
|
51
85
|
return yield* (toolkitHandler.handle as any)(tool.name, input);
|
|
52
86
|
}
|
|
53
87
|
|
|
54
|
-
const {
|
|
55
|
-
const functionDef = functions.find((fn) => fn.name === functionName);
|
|
56
|
-
if (!functionDef) {
|
|
57
|
-
return yield* Effect.fail(new AiToolNotFoundError(tool.name));
|
|
58
|
-
}
|
|
88
|
+
const { definition: functionDef } = Context.get(FunctionToolAnnotation)(tool.annotations as any);
|
|
59
89
|
|
|
60
|
-
return yield*
|
|
61
|
-
.invokeFunction(functionDef, input)
|
|
90
|
+
return yield* functionInvocationService
|
|
91
|
+
.invokeFunction(functionDef, input as any)
|
|
62
92
|
.pipe(Effect.catchAllDefect((defect) => Effect.fail(defect)));
|
|
63
93
|
});
|
|
64
94
|
};
|
|
65
95
|
|
|
66
|
-
return toolkit.of(
|
|
96
|
+
return toolkit.of(
|
|
97
|
+
Record.map(toolkit.tools, (tool, _name) => (Tool.isUserDefined(tool) ? makeHandler(tool) : null)) as any,
|
|
98
|
+
) as any;
|
|
67
99
|
},
|
|
68
100
|
};
|
|
69
101
|
}),
|
|
70
102
|
);
|
|
71
103
|
};
|
|
72
104
|
|
|
73
|
-
|
|
74
|
-
* Tools that are projected from functions have this annotation.
|
|
75
|
-
*/
|
|
76
|
-
class FunctionToolAnnotation extends Context.Tag('@dxos/assisatnt/FunctionToolAnnotation')<
|
|
105
|
+
class FunctionToolAnnotation extends Context.Tag('@dxos/assistant/FunctionToolAnnotation')<
|
|
77
106
|
FunctionToolAnnotation,
|
|
78
|
-
{
|
|
79
|
-
functionName: string;
|
|
80
|
-
}
|
|
107
|
+
{ definition: FunctionDefinition<any, any> }
|
|
81
108
|
>() {}
|
|
82
109
|
|
|
83
|
-
const toolCache = new WeakMap<FunctionDefinition<any, any>,
|
|
110
|
+
const toolCache = new WeakMap<FunctionDefinition<any, any>, Tool.Any>();
|
|
84
111
|
|
|
85
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Projects a `FunctionDefinition` into an `AiTool`.
|
|
114
|
+
*
|
|
115
|
+
* @param fn The function definition to project into a tool.
|
|
116
|
+
* @param meta Optional projection metadata.
|
|
117
|
+
* @param meta.deployedFunctionId Backend deployment ID used for remote invocation when present. This is the
|
|
118
|
+
* EDGE service's function deployment identifier (not the ECHO object ID/DXN and not `FunctionDefinition.key`).
|
|
119
|
+
*/
|
|
120
|
+
const projectFunctionToTool = (fn: FunctionDefinition<any, any>): Tool.Any => {
|
|
86
121
|
if (toolCache.has(fn)) {
|
|
87
122
|
return toolCache.get(fn)!;
|
|
88
123
|
}
|
|
89
124
|
|
|
90
|
-
const tool =
|
|
125
|
+
const tool = Tool.make(makeToolName(fn.name), {
|
|
91
126
|
description: fn.description,
|
|
92
127
|
parameters: createStructFieldsFromSchema(fn.inputSchema),
|
|
93
128
|
// TODO(dmaretskyi): Include output schema.
|
|
94
129
|
failure: Schema.Any, // TODO(dmaretskyi): Better type for the failure?
|
|
95
|
-
}).annotate(FunctionToolAnnotation, {
|
|
130
|
+
}).annotate(FunctionToolAnnotation, { definition: fn });
|
|
96
131
|
toolCache.set(fn, tool);
|
|
97
132
|
return tool;
|
|
98
133
|
};
|
|
@@ -117,3 +152,7 @@ const createStructFieldsFromSchema = (schema: Schema.Schema<any, any>): Record<s
|
|
|
117
152
|
return todo(`Unsupported schema AST: ${schema.ast._tag}`);
|
|
118
153
|
}
|
|
119
154
|
};
|
|
155
|
+
|
|
156
|
+
const isHandlerLike = (value: unknown): value is Toolkit.WithHandler<Record<string, Tool.Any>> => {
|
|
157
|
+
return typeof (value as any).tools === 'object' && typeof (value as any).handle === 'function';
|
|
158
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
export * from './conversation';
|
|
6
6
|
export * from './diff';
|
|
7
|
+
export * from './errors';
|
|
8
|
+
export * from './execution-graph';
|
|
7
9
|
export * from './functions';
|
|
8
10
|
export * from './session';
|
|
9
11
|
export * from './templates';
|
|
10
12
|
export * from './types';
|
|
11
|
-
export * from './errors';
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Context from 'effect/Context';
|
|
6
6
|
|
|
7
|
+
import { type ObjectId } from '@dxos/echo/internal';
|
|
7
8
|
import { type ObjectVersion } from '@dxos/echo-db';
|
|
8
|
-
import { type ObjectId } from '@dxos/echo-schema';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Resolves artifact ids to their versions.
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import * as AiError from '@effect/ai/AiError';
|
|
6
|
+
import * as HttpClientError from '@effect/platform/HttpClientError';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
8
9
|
|
|
9
10
|
import { AiModelError } from '../errors';
|
|
10
11
|
|
|
@@ -16,6 +17,7 @@ const AnthropicErrorResponse = Schema.Struct({
|
|
|
16
17
|
}),
|
|
17
18
|
});
|
|
18
19
|
|
|
20
|
+
// TODO(dmaretskyi): Needs rework
|
|
19
21
|
export const mapAiError = (err: AiError.AiError): Effect.Effect<AiError.AiError> =>
|
|
20
22
|
Effect.gen(function* () {
|
|
21
23
|
const cause = err.cause;
|
|
@@ -28,7 +30,7 @@ export const mapAiError = (err: AiError.AiError): Effect.Effect<AiError.AiError>
|
|
|
28
30
|
message: body.error.message,
|
|
29
31
|
context: { model: 'anthropic', type: body.error.type },
|
|
30
32
|
});
|
|
31
|
-
return
|
|
33
|
+
return AiError.UnknownError.make({
|
|
32
34
|
description: body.error.message,
|
|
33
35
|
module: err.module,
|
|
34
36
|
method: err.method,
|
package/src/session/format.ts
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Array from 'effect/Array';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import * as Function from 'effect/Function';
|
|
8
|
+
import * as Option from 'effect/Option';
|
|
6
9
|
|
|
7
10
|
import { Template } from '@dxos/blueprints';
|
|
8
11
|
import { Obj } from '@dxos/echo';
|
|
12
|
+
import { type ObjectId } from '@dxos/echo/internal';
|
|
9
13
|
import { ObjectVersion } from '@dxos/echo-db';
|
|
10
|
-
import { type ObjectId } from '@dxos/echo-schema';
|
|
11
14
|
import { DatabaseService } from '@dxos/functions';
|
|
12
15
|
import { log } from '@dxos/log';
|
|
13
16
|
import { type ContentBlock, DataType } from '@dxos/schema';
|
|
@@ -28,7 +31,7 @@ export const formatSystemPrompt = ({
|
|
|
28
31
|
objects = [],
|
|
29
32
|
}: Pick<AiSessionRunParams<any>, 'system' | 'blueprints' | 'objects'>) =>
|
|
30
33
|
Effect.gen(function* () {
|
|
31
|
-
const blueprintDefs = yield* pipe(
|
|
34
|
+
const blueprintDefs = yield* Function.pipe(
|
|
32
35
|
blueprints,
|
|
33
36
|
Effect.forEach((blueprint) => Effect.succeed(blueprint.instructions)),
|
|
34
37
|
Effect.flatMap(Effect.forEach((template) => DatabaseService.loadOption(template.source))),
|
|
@@ -47,7 +50,7 @@ export const formatSystemPrompt = ({
|
|
|
47
50
|
),
|
|
48
51
|
);
|
|
49
52
|
|
|
50
|
-
const objectDefs = yield* pipe(
|
|
53
|
+
const objectDefs = yield* Function.pipe(
|
|
51
54
|
objects,
|
|
52
55
|
Effect.forEach((object) =>
|
|
53
56
|
Effect.succeed(trim`
|
|
@@ -60,7 +63,7 @@ export const formatSystemPrompt = ({
|
|
|
60
63
|
Effect.map((objects) => (objects.length > 0 ? ['## Context Objects', ...objects].join('\n\n') : undefined)),
|
|
61
64
|
);
|
|
62
65
|
|
|
63
|
-
return yield* pipe(
|
|
66
|
+
return yield* Function.pipe(
|
|
64
67
|
Effect.succeed([system, blueprintDefs, objectDefs].filter((def): def is string => def !== undefined)),
|
|
65
68
|
Effect.map((parts) => parts.join('\n\n')),
|
|
66
69
|
);
|
package/src/session/observer.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Function from 'effect/Function';
|
|
6
7
|
|
|
7
8
|
import { type AiParser, type ConsolePrinter } from '@dxos/ai';
|
|
8
9
|
import { type DataType } from '@dxos/schema';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"conversations":[{"parameters":{"model":"@anthropic/claude-3-5-sonnet-20241022","stream":true,"tools":[]},"prompt":{"content":[{"role":"user","content":"Hello world!","options":{}}]},"response":[{"type":"response-metadata","id":"msg_01Bm496JGgDMgC9YwdYTFFHo","modelId":"claude-3-5-sonnet-20241022","timestamp":"1970-01-01T00:00:00.000Z","metadata":{}},{"type":"text-start","id":"0","metadata":{}},{"type":"text-delta","id":"0","delta":"Hi","metadata":{}},{"type":"text-delta","id":"0","delta":"!","metadata":{}},{"type":"text-delta","id":"0","delta":" How can","metadata":{}},{"type":"text-delta","id":"0","delta":" I help you today?","metadata":{}},{"type":"text-end","id":"0","metadata":{}},{"type":"finish","reason":"stop","usage":{"inputTokens":10,"outputTokens":12,"totalTokens":22},"metadata":{"anthropic":{"usage":{"cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"input_tokens":10,"output_tokens":1,"server_tool_use":null,"service_tier":"standard"}}}}]},{"parameters":{"model":"@anthropic/claude-3-5-sonnet-20241022","stream":true,"tools":[{"name":"Calculator","description":"Basic calculator tool","inputSchema":{"type":"object","required":["input"],"properties":{"input":{"type":"string","description":"The calculation to perform."}},"additionalProperties":false}}]},"prompt":{"content":[{"role":"user","content":"What is 10 + 30?","options":{}}]},"response":[{"type":"response-metadata","id":"msg_01UNTbjtTHEpdFgGunu2k8Br","modelId":"claude-3-5-sonnet-20241022","timestamp":"1970-01-01T00:00:00.000Z","metadata":{}},{"type":"text-start","id":"0","metadata":{}},{"type":"text-delta","id":"0","delta":"I","metadata":{}},{"type":"text-delta","id":"0","delta":"'ll help","metadata":{}},{"type":"text-delta","id":"0","delta":" you calculate","metadata":{}},{"type":"text-delta","id":"0","delta":" ","metadata":{}},{"type":"text-delta","id":"0","delta":"10 + 30","metadata":{}},{"type":"text-delta","id":"0","delta":" using the","metadata":{}},{"type":"text-delta","id":"0","delta":" Calculator tool.","metadata":{}},{"type":"text-end","id":"0","metadata":{}},{"type":"tool-params-start","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","name":"Calculator","providerExecuted":false,"metadata":{}},{"type":"tool-params-delta","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","delta":"","metadata":{}},{"type":"tool-params-delta","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","delta":"{\"input\": \"","metadata":{}},{"type":"tool-params-delta","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","delta":"10 + 30","metadata":{}},{"type":"tool-params-delta","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","delta":"\"}","metadata":{}},{"type":"tool-params-end","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","metadata":{}},{"type":"tool-call","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","name":"Calculator","params":{"input":"10 + 30"},"providerExecuted":false,"metadata":{}},{"type":"finish","reason":"tool-calls","usage":{"inputTokens":394,"outputTokens":73,"totalTokens":467},"metadata":{"anthropic":{"usage":{"cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"input_tokens":394,"output_tokens":1,"server_tool_use":null,"service_tier":"standard"}}}}]},{"parameters":{"model":"@anthropic/claude-3-5-sonnet-20241022","stream":true,"tools":[{"name":"Calculator","description":"Basic calculator tool","inputSchema":{"type":"object","required":["input"],"properties":{"input":{"type":"string","description":"The calculation to perform."}},"additionalProperties":false}}]},"prompt":{"content":[{"role":"user","content":"What is 10 + 30?","options":{}},{"role":"assistant","content":[{"type":"text","text":"I'll help you calculate 10 + 30 using the Calculator tool.","options":{}},{"type":"tool-call","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","name":"Calculator","params":{"input":"10 + 30"},"providerExecuted":false,"options":{}}],"options":{}},{"role":"tool","content":[{"type":"tool-result","id":"toolu_01LTGUgfihCQtVchm1b37xHQ","name":"Calculator","result":{"result":40},"options":{}}],"options":{}}]},"response":[{"type":"response-metadata","id":"msg_01AKio3GheJGn38BhvAp33rs","modelId":"claude-3-5-sonnet-20241022","timestamp":"1970-01-01T00:00:00.000Z","metadata":{}},{"type":"text-start","id":"0","metadata":{}},{"type":"text-delta","id":"0","delta":"The result","metadata":{}},{"type":"text-delta","id":"0","delta":" of 10 +","metadata":{}},{"type":"text-delta","id":"0","delta":" 30 is ","metadata":{}},{"type":"text-delta","id":"0","delta":"40.","metadata":{}},{"type":"text-end","id":"0","metadata":{}},{"type":"finish","reason":"stop","usage":{"inputTokens":482,"outputTokens":18,"totalTokens":500},"metadata":{"anthropic":{"usage":{"cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"input_tokens":482,"output_tokens":3,"server_tool_use":null,"service_tier":"standard"}}}}]}]}
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Tool from '@effect/ai/Tool';
|
|
6
|
+
import * as Toolkit from '@effect/ai/Toolkit';
|
|
6
7
|
import { describe, it } from '@effect/vitest';
|
|
7
|
-
import
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
import * as Layer from 'effect/Layer';
|
|
10
|
+
import * as Schema from 'effect/Schema';
|
|
8
11
|
|
|
9
|
-
import { AiService, ToolExecutionService, ToolResolverService } from '@dxos/ai';
|
|
12
|
+
import { AiService, MemoizedAiService, ToolExecutionService, ToolResolverService } from '@dxos/ai';
|
|
10
13
|
import { AiServiceTestingPreset } from '@dxos/ai/testing';
|
|
11
14
|
import { Obj, Type } from '@dxos/echo';
|
|
15
|
+
import { TestHelpers } from '@dxos/effect';
|
|
12
16
|
import { TracingService } from '@dxos/functions';
|
|
13
17
|
import { log } from '@dxos/log';
|
|
14
18
|
|
|
@@ -29,8 +33,8 @@ const CalendarEventSchema = Schema.Struct({
|
|
|
29
33
|
|
|
30
34
|
type CalendarEvent = Schema.Schema.Type<typeof CalendarEventSchema>;
|
|
31
35
|
|
|
32
|
-
class TestToolkit extends
|
|
33
|
-
|
|
36
|
+
class TestToolkit extends Toolkit.make(
|
|
37
|
+
Tool.make('Calculator', {
|
|
34
38
|
description: 'Basic calculator tool',
|
|
35
39
|
parameters: {
|
|
36
40
|
input: Schema.String.annotations({
|
|
@@ -61,175 +65,52 @@ const toolkitLayer = TestToolkit.toLayer({
|
|
|
61
65
|
}),
|
|
62
66
|
});
|
|
63
67
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Layer.mergeAll(
|
|
76
|
-
toolkitLayer,
|
|
77
|
-
AiService.model('@anthropic/claude-3-5-sonnet-20241022').pipe(
|
|
78
|
-
Layer.provideMerge(ToolResolverService.layerEmpty),
|
|
79
|
-
Layer.provideMerge(ToolExecutionService.layerEmpty),
|
|
80
|
-
Layer.provideMerge(AiServiceTestingPreset('direct')),
|
|
81
|
-
Layer.provideMerge(TracingService.layerNoop),
|
|
82
|
-
),
|
|
83
|
-
),
|
|
84
|
-
),
|
|
85
|
-
),
|
|
86
|
-
);
|
|
68
|
+
const TestLayer = Layer.mergeAll(
|
|
69
|
+
toolkitLayer,
|
|
70
|
+
AiService.model('@anthropic/claude-3-5-sonnet-20241022'),
|
|
71
|
+
TracingService.layerNoop,
|
|
72
|
+
ToolResolverService.layerEmpty,
|
|
73
|
+
ToolExecutionService.layerEmpty,
|
|
74
|
+
).pipe(
|
|
75
|
+
//
|
|
76
|
+
Layer.provideMerge(MemoizedAiService.layerTest()),
|
|
77
|
+
Layer.provide(AiServiceTestingPreset('direct')),
|
|
78
|
+
);
|
|
87
79
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
AiService.model('@anthropic/claude-3-5-sonnet-20241022').pipe(
|
|
103
|
-
Layer.provideMerge(AiServiceTestingPreset('direct')),
|
|
104
|
-
),
|
|
105
|
-
ToolResolverService.layerEmpty,
|
|
106
|
-
ToolExecutionService.layerEmpty,
|
|
107
|
-
toolkitLayer,
|
|
108
|
-
),
|
|
109
|
-
),
|
|
80
|
+
describe('AiSession', () => {
|
|
81
|
+
it.effect(
|
|
82
|
+
'no tools',
|
|
83
|
+
Effect.fnUntraced(
|
|
84
|
+
function* (_) {
|
|
85
|
+
const session = new AiSession({ operationModel: 'configured' });
|
|
86
|
+
const response = yield* session.run({
|
|
87
|
+
prompt: 'Hello world!',
|
|
88
|
+
history: [],
|
|
89
|
+
});
|
|
90
|
+
log.info('response', { response });
|
|
91
|
+
},
|
|
92
|
+
Effect.provide(TestLayer),
|
|
93
|
+
TestHelpers.provideTestContext,
|
|
110
94
|
),
|
|
111
95
|
);
|
|
112
96
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
schema: CalendarEventSchema,
|
|
126
|
-
tools: [
|
|
127
|
-
createTool('calendar', {
|
|
128
|
-
name: 'query',
|
|
129
|
-
description: 'Query the calendar for events',
|
|
130
|
-
schema: Schema.Struct({}),
|
|
131
|
-
execute: async () => {
|
|
132
|
-
return ToolResult.Success(CALENDAR_EVENTS);
|
|
133
|
-
},
|
|
134
|
-
}),
|
|
135
|
-
],
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const tableArtifact = defineArtifact({
|
|
139
|
-
id: 'artifact:dxos.org/plugin/table',
|
|
140
|
-
name: 'Table',
|
|
141
|
-
instructions: 'Use this to create and manage tables. Each table has a unique id.',
|
|
142
|
-
schema: Schema.Struct({}),
|
|
143
|
-
tools: [
|
|
144
|
-
createTool('table', {
|
|
145
|
-
name: 'create',
|
|
146
|
-
description: 'Create a table',
|
|
147
|
-
schema: Schema.Struct({
|
|
148
|
-
data: Schema.Array(Schema.Any).annotations({ description: 'Array of data payloads to add as rows' }),
|
|
149
|
-
}),
|
|
150
|
-
execute: async ({ data }) => {
|
|
151
|
-
log('create table', { data });
|
|
152
|
-
const id = DXN.fromLocalObjectId(ObjectId.random()).toString();
|
|
153
|
-
objects.add(id);
|
|
154
|
-
// TODO(dmaretskyi): consider xml for refs instead of @dxn:echo:@:XXXXX
|
|
155
|
-
return ToolResult.Success(`table @${id}`);
|
|
156
|
-
},
|
|
157
|
-
}),
|
|
158
|
-
],
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const mapArtifact = defineArtifact({
|
|
162
|
-
id: 'artifact:dxos.org/plugin/map',
|
|
163
|
-
name: 'Map',
|
|
164
|
-
instructions:
|
|
165
|
-
'Use this to create and manage maps. Maps source data from tables. Table id is required to create a map.',
|
|
166
|
-
schema: Schema.Struct({}),
|
|
167
|
-
tools: [
|
|
168
|
-
createTool('map', {
|
|
169
|
-
name: 'create',
|
|
170
|
-
description: 'Create a map',
|
|
171
|
-
schema: Schema.Struct({
|
|
172
|
-
source: ArtifactId.annotations({
|
|
173
|
-
description: 'The table that will be used as the source of the map',
|
|
174
|
-
}),
|
|
175
|
-
}),
|
|
176
|
-
execute: async ({ source }) => {
|
|
177
|
-
// TODO(dmaretskyi): Use effect-schema decode instead of manual parsing.
|
|
178
|
-
const sourceId = ArtifactId.toDXN(source);
|
|
179
|
-
if (!objects.has(sourceId.toString())) {
|
|
180
|
-
return ToolResult.Error(`table id=${source} not found`);
|
|
181
|
-
}
|
|
182
|
-
log('create map', { sourceId });
|
|
183
|
-
const id = DXN.fromLocalObjectId(ObjectId.random()).toString();
|
|
184
|
-
objects.add(id);
|
|
185
|
-
// TODO(dmaretskyi): consider xml for refs instead of @dxn:echo:@:XXXXX
|
|
186
|
-
return ToolResult.Success(`map @${id}`);
|
|
187
|
-
},
|
|
188
|
-
}),
|
|
189
|
-
],
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
const scriptArtifact = defineArtifact({
|
|
193
|
-
id: 'artifact:dxos.org/plugin/script',
|
|
194
|
-
name: 'Script',
|
|
195
|
-
instructions: 'Use this to create and manage scripts',
|
|
196
|
-
schema: Schema.Struct({}),
|
|
197
|
-
tools: [],
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// session.streamEvent.on((event) => {
|
|
201
|
-
// printStreamEvent(event);
|
|
202
|
-
// });
|
|
203
|
-
|
|
204
|
-
const printer = new ConsolePrinter();
|
|
205
|
-
session.message.on((message) => printer.printMessage(message));
|
|
206
|
-
session.userMessage.on((message) => printer.printMessage(message));
|
|
207
|
-
session.block.on((block) => printer.printContentBlock(block));
|
|
208
|
-
|
|
209
|
-
// session.update.on((update) => {
|
|
210
|
-
// log('update', { update });
|
|
211
|
-
// });
|
|
212
|
-
|
|
213
|
-
// Test creating an itinerary
|
|
214
|
-
const response = await session.run({
|
|
215
|
-
client: aiClient,
|
|
216
|
-
tools: [],
|
|
217
|
-
artifacts: [calendarArtifact, tableArtifact, mapArtifact, scriptArtifact],
|
|
218
|
-
requiredArtifactIds: [calendarArtifact.id, tableArtifact.id, mapArtifact.id, scriptArtifact.id],
|
|
219
|
-
history: [],
|
|
220
|
-
generationOptions: {
|
|
221
|
-
model: '@anthropic/claude-3-5-haiku-20241022',
|
|
97
|
+
it.effect(
|
|
98
|
+
'calculator',
|
|
99
|
+
Effect.fnUntraced(
|
|
100
|
+
function* (_) {
|
|
101
|
+
const session = new AiSession({ operationModel: 'configured' });
|
|
102
|
+
const toolkit = yield* TestToolkit;
|
|
103
|
+
const response = yield* session.run({
|
|
104
|
+
toolkit,
|
|
105
|
+
prompt: 'What is 10 + 30?',
|
|
106
|
+
history: [],
|
|
107
|
+
});
|
|
108
|
+
log.info('response', { response });
|
|
222
109
|
},
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
log('result', {
|
|
228
|
-
objects,
|
|
229
|
-
finalMessage: response.at(-1),
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
*/
|
|
110
|
+
Effect.provide(TestLayer),
|
|
111
|
+
TestHelpers.provideTestContext,
|
|
112
|
+
),
|
|
113
|
+
);
|
|
233
114
|
});
|
|
234
115
|
|
|
235
116
|
// Travel to rome, florence, livorno, siena, madrid for conferences
|
|
@@ -266,43 +147,3 @@ const _CALENDAR_EVENTS: CalendarEvent[] = [
|
|
|
266
147
|
description: 'Travel to Madrid',
|
|
267
148
|
}),
|
|
268
149
|
];
|
|
269
|
-
|
|
270
|
-
// const printStreamEvent = (event: GenerationStreamEvent) => {
|
|
271
|
-
// switch (event.type) {
|
|
272
|
-
// case 'message_start': {
|
|
273
|
-
// process.stdout.write(`${event.message.role.toUpperCase()}\n\n`);
|
|
274
|
-
// for (const content of event.message.content) {
|
|
275
|
-
// printContentBlock(content);
|
|
276
|
-
// }
|
|
277
|
-
// break;
|
|
278
|
-
// }
|
|
279
|
-
// case 'content_block_start': {
|
|
280
|
-
// printContentBlock(event.content);
|
|
281
|
-
// break;
|
|
282
|
-
// }
|
|
283
|
-
// case 'content_block_delta': {
|
|
284
|
-
// switch (event.delta.type) {
|
|
285
|
-
// case 'text_delta': {
|
|
286
|
-
// process.stdout.write(event.delta.text);
|
|
287
|
-
// break;
|
|
288
|
-
// }
|
|
289
|
-
// case 'input_json_delta': {
|
|
290
|
-
// process.stdout.write(event.delta.partial_json);
|
|
291
|
-
// break;
|
|
292
|
-
// }
|
|
293
|
-
// }
|
|
294
|
-
// break;
|
|
295
|
-
// }
|
|
296
|
-
// case 'content_block_stop': {
|
|
297
|
-
// process.stdout.write('\n');
|
|
298
|
-
// break;
|
|
299
|
-
// }
|
|
300
|
-
// case 'message_delta': {
|
|
301
|
-
// break;
|
|
302
|
-
// }
|
|
303
|
-
// case 'message_stop': {
|
|
304
|
-
// process.stdout.write('\n\n');
|
|
305
|
-
// break;
|
|
306
|
-
// }
|
|
307
|
-
// }
|
|
308
|
-
// };
|