@dxos/assistant 0.8.4-main.dedc0f3 → 0.8.4-main.dfabb4ec29

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.
Files changed (152) hide show
  1. package/README.md +1 -0
  2. package/dist/lib/neutral/extraction/index.mjs +202 -0
  3. package/dist/lib/neutral/extraction/index.mjs.map +7 -0
  4. package/dist/lib/neutral/index.mjs +1736 -0
  5. package/dist/lib/neutral/index.mjs.map +7 -0
  6. package/dist/lib/neutral/meta.json +1 -0
  7. package/dist/types/src/conversation/context.d.ts +124 -64
  8. package/dist/types/src/conversation/context.d.ts.map +1 -1
  9. package/dist/types/src/conversation/context.test.d.ts +2 -0
  10. package/dist/types/src/conversation/context.test.d.ts.map +1 -0
  11. package/dist/types/src/conversation/index.d.ts +1 -1
  12. package/dist/types/src/conversation/index.d.ts.map +1 -1
  13. package/dist/types/src/conversation/session.d.ts +73 -0
  14. package/dist/types/src/conversation/session.d.ts.map +1 -0
  15. package/dist/types/src/diff.d.ts +3 -1
  16. package/dist/types/src/diff.d.ts.map +1 -1
  17. package/dist/types/src/errors.d.ts +40 -48
  18. package/dist/types/src/errors.d.ts.map +1 -1
  19. package/dist/types/src/execution-graph.d.ts +85 -0
  20. package/dist/types/src/execution-graph.d.ts.map +1 -0
  21. package/dist/types/src/execution-graph.test.d.ts +2 -0
  22. package/dist/types/src/execution-graph.test.d.ts.map +1 -0
  23. package/dist/types/src/extraction/extraction-ner-function/index.d.ts +0 -1
  24. package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -1
  25. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts.map +1 -1
  26. package/dist/types/src/extraction/extraction.d.ts +1066 -303
  27. package/dist/types/src/extraction/extraction.d.ts.map +1 -1
  28. package/dist/types/src/extraction/index.d.ts +0 -1
  29. package/dist/types/src/extraction/index.d.ts.map +1 -1
  30. package/dist/types/src/extraction/quotes.d.ts +3 -3
  31. package/dist/types/src/extraction/quotes.d.ts.map +1 -1
  32. package/dist/types/src/functions/services.d.ts +15 -5
  33. package/dist/types/src/functions/services.d.ts.map +1 -1
  34. package/dist/types/src/index.d.ts +3 -1
  35. package/dist/types/src/index.d.ts.map +1 -1
  36. package/dist/types/src/session/artifact-diff.d.ts +2 -2
  37. package/dist/types/src/session/artifact-diff.d.ts.map +1 -1
  38. package/dist/types/src/session/error-handling.d.ts +2 -2
  39. package/dist/types/src/session/error-handling.d.ts.map +1 -1
  40. package/dist/types/src/session/format.d.ts +8 -175
  41. package/dist/types/src/session/format.d.ts.map +1 -1
  42. package/dist/types/src/session/index.d.ts +2 -1
  43. package/dist/types/src/session/index.d.ts.map +1 -1
  44. package/dist/types/src/session/observer.d.ts +7 -6
  45. package/dist/types/src/session/observer.d.ts.map +1 -1
  46. package/dist/types/src/session/request.d.ts +104 -0
  47. package/dist/types/src/session/request.d.ts.map +1 -0
  48. package/dist/types/src/session/toolkit.d.ts +11 -9
  49. package/dist/types/src/session/toolkit.d.ts.map +1 -1
  50. package/dist/types/src/session/version-pin.d.ts +4 -4
  51. package/dist/types/src/session/version-pin.d.ts.map +1 -1
  52. package/dist/types/src/templates/system.d.ts +1 -2
  53. package/dist/types/src/templates/system.d.ts.map +1 -1
  54. package/dist/types/src/tracing.d.ts +233 -0
  55. package/dist/types/src/tracing.d.ts.map +1 -0
  56. package/dist/types/src/types/artifact.d.ts +28 -0
  57. package/dist/types/src/types/artifact.d.ts.map +1 -0
  58. package/dist/types/src/types/index.d.ts +2 -0
  59. package/dist/types/src/types/index.d.ts.map +1 -0
  60. package/dist/types/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +35 -46
  62. package/src/conversation/context.test.ts +52 -0
  63. package/src/conversation/context.ts +324 -75
  64. package/src/conversation/index.ts +1 -1
  65. package/src/conversation/session.ts +323 -0
  66. package/src/diff.test.ts +37 -6
  67. package/src/diff.ts +18 -4
  68. package/src/errors.ts +4 -4
  69. package/src/execution-graph.test.ts +740 -0
  70. package/src/execution-graph.ts +519 -0
  71. package/src/extraction/extraction-ner-function/index.ts +0 -1
  72. package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +12 -6
  73. package/src/extraction/extraction.ts +44 -15
  74. package/src/extraction/index.ts +0 -1
  75. package/src/extraction/quotes.test.ts +2 -2
  76. package/src/extraction/quotes.ts +6 -7
  77. package/src/functions/services.ts +156 -61
  78. package/src/index.ts +3 -1
  79. package/src/session/artifact-diff.ts +2 -2
  80. package/src/session/error-handling.ts +11 -6
  81. package/src/session/format.ts +35 -27
  82. package/src/session/index.ts +2 -1
  83. package/src/session/observer.ts +8 -4
  84. package/src/session/request.ts +347 -0
  85. package/src/session/toolkit.ts +32 -27
  86. package/src/session/version-pin.ts +7 -7
  87. package/src/templates/{format.tpl → instructions/format.tpl} +1 -4
  88. package/src/templates/instructions/system.tpl +96 -0
  89. package/src/templates/system.ts +9 -8
  90. package/src/tracing.ts +57 -0
  91. package/src/{types.ts → types/artifact.ts} +44 -7
  92. package/src/types/index.ts +5 -0
  93. package/dist/lib/browser/chunk-5GJYG2ML.mjs +0 -396
  94. package/dist/lib/browser/chunk-5GJYG2ML.mjs.map +0 -7
  95. package/dist/lib/browser/chunk-UB26N5UF.mjs +0 -89
  96. package/dist/lib/browser/chunk-UB26N5UF.mjs.map +0 -7
  97. package/dist/lib/browser/extraction/index.mjs +0 -268
  98. package/dist/lib/browser/extraction/index.mjs.map +0 -7
  99. package/dist/lib/browser/index.mjs +0 -469
  100. package/dist/lib/browser/index.mjs.map +0 -7
  101. package/dist/lib/browser/meta.json +0 -1
  102. package/dist/lib/browser/named-entity-recognition-QPYHML66.mjs +0 -13
  103. package/dist/lib/browser/named-entity-recognition-QPYHML66.mjs.map +0 -7
  104. package/dist/lib/node-esm/chunk-BCFXTNSK.mjs +0 -91
  105. package/dist/lib/node-esm/chunk-BCFXTNSK.mjs.map +0 -7
  106. package/dist/lib/node-esm/chunk-FLFCEB4P.mjs +0 -398
  107. package/dist/lib/node-esm/chunk-FLFCEB4P.mjs.map +0 -7
  108. package/dist/lib/node-esm/extraction/index.mjs +0 -269
  109. package/dist/lib/node-esm/extraction/index.mjs.map +0 -7
  110. package/dist/lib/node-esm/index.mjs +0 -470
  111. package/dist/lib/node-esm/index.mjs.map +0 -7
  112. package/dist/lib/node-esm/meta.json +0 -1
  113. package/dist/lib/node-esm/named-entity-recognition-2HDF46YX.mjs +0 -14
  114. package/dist/lib/node-esm/named-entity-recognition-2HDF46YX.mjs.map +0 -7
  115. package/dist/types/src/conversation/conversation.d.ts +0 -42
  116. package/dist/types/src/conversation/conversation.d.ts.map +0 -1
  117. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +0 -4
  118. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +0 -1
  119. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +0 -2
  120. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +0 -1
  121. package/dist/types/src/extraction/extraction-llm-function/index.d.ts +0 -2
  122. package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +0 -1
  123. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +0 -337
  124. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +0 -1
  125. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +0 -2
  126. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +0 -1
  127. package/dist/types/src/session/format.test.d.ts +0 -2
  128. package/dist/types/src/session/format.test.d.ts.map +0 -1
  129. package/dist/types/src/session/session.d.ts +0 -47
  130. package/dist/types/src/session/session.d.ts.map +0 -1
  131. package/dist/types/src/session/session.test.d.ts +0 -2
  132. package/dist/types/src/session/session.test.d.ts.map +0 -1
  133. package/dist/types/src/session/version-pin.test.d.ts +0 -2
  134. package/dist/types/src/session/version-pin.test.d.ts.map +0 -1
  135. package/dist/types/src/summarization/summarization.test.d.ts +0 -2
  136. package/dist/types/src/summarization/summarization.test.d.ts.map +0 -1
  137. package/dist/types/src/types.d.ts +0 -16
  138. package/dist/types/src/types.d.ts.map +0 -1
  139. package/src/conversation/conversation.ts +0 -123
  140. package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +0 -84
  141. package/src/extraction/extraction-llm-function/extraction-llm-function.ts +0 -70
  142. package/src/extraction/extraction-llm-function/index.ts +0 -5
  143. package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +0 -115
  144. package/src/extraction/extraction-ner-function/extraction-ner-function.ts +0 -62
  145. package/src/session/__snapshots__/session.test.ts.snap +0 -32
  146. package/src/session/format.test.ts +0 -60
  147. package/src/session/session.test.ts +0 -308
  148. package/src/session/session.ts +0 -201
  149. package/src/session/version-pin.test.ts +0 -7
  150. package/src/summarization/summarization.test.ts +0 -197
  151. package/src/templates/system.tpl +0 -50
  152. /package/src/extraction/extraction-llm-function/{instructions.tpl → transcription-instructions.tpl} +0 -0
@@ -2,11 +2,10 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
 
7
- import { Filter, Query } from '@dxos/echo';
8
- import { type EchoDatabase } from '@dxos/echo-db';
9
- import { ObjectId } from '@dxos/echo-schema';
7
+ import { type Database, Filter, Query } from '@dxos/echo';
8
+ import { ObjectId } from '@dxos/keys';
10
9
  import { DXN } from '@dxos/keys';
11
10
  import { log } from '@dxos/log';
12
11
 
@@ -28,10 +27,10 @@ export const ReferencedQuotes = Schema.Struct({
28
27
  });
29
28
  export type ReferencedQuotes = Schema.Schema.Type<typeof ReferencedQuotes>;
30
29
 
31
- export const findQuotes = async (quotes: string[], db: EchoDatabase): Promise<ReferencedQuotes> => {
30
+ export const findQuotes = async (quotes: string[], db: Database.Database): Promise<ReferencedQuotes> => {
32
31
  const references = await Promise.all(
33
32
  quotes.map(async (quote) => {
34
- const { objects } = await db.query(Query.select(Filter.text(quote, { type: 'vector' }))).run();
33
+ const objects = await db.query(Query.select(Filter.text(quote, { type: 'vector' }))).run();
35
34
  log.info('found quotes in vector index', { objects, quote });
36
35
  return objects.length > 0 ? [{ id: objects[0].id.toString(), quote }] : [];
37
36
  }),
@@ -51,7 +50,7 @@ export const insertReferences = (text: string, quotes: ReferencedQuotes) => {
51
50
 
52
51
  // Use a case-insensitive regular expression to replace the quote.
53
52
  const regex = new RegExp(quote.quote.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
54
- text = text.replace(regex, `[${quote.quote}][${DXN.fromLocalObjectId(quote.id).toString()}]`);
53
+ text = text.replace(regex, `[${quote.quote}](${DXN.fromLocalObjectId(quote.id).toString()})`);
55
54
  }
56
55
 
57
56
  return text;
@@ -2,97 +2,149 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { AiTool, type AiToolkit } from '@effect/ai';
6
- import { Context, Effect, Layer, Record, Schema } from 'effect';
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 Option from 'effect/Option';
11
+ import * as Record from 'effect/Record';
12
+ import * as Schema from 'effect/Schema';
13
+ import * as SchemaAST from 'effect/SchemaAST';
7
14
 
8
15
  import { AiToolNotFoundError, ToolExecutionService, ToolResolverService } from '@dxos/ai';
16
+ import { OpaqueToolkit } from '@dxos/ai';
17
+ import { Operation, OperationRegistry } from '@dxos/compute';
9
18
  import { todo } from '@dxos/debug';
10
- import { type FunctionDefinition, LocalFunctionExecutionService } from '@dxos/functions';
19
+ import { Ref } from '@dxos/echo';
11
20
  import { invariant } from '@dxos/invariant';
12
21
 
13
- export const makeToolResolverFromFunctions = (
14
- functions: FunctionDefinition<any, any>[],
15
- toolkit: AiToolkit.Any,
16
- ): Layer.Layer<ToolResolverService> => {
17
- return Layer.succeed(ToolResolverService, {
18
- resolve: Effect.fn('resolveTool')(function* (id) {
19
- const tool = toolkit.tools[id];
20
- if (tool) {
21
- return tool;
22
- }
23
-
24
- const functionDef = functions.find((fn) => fn.name === id);
25
- if (!functionDef) {
26
- return yield* Effect.fail(new AiToolNotFoundError(id));
27
- }
28
-
29
- return projectFunctionToTool(functionDef);
22
+ import { RefFromLLM } from '../types';
23
+
24
+ export const makeToolResolverFromOperations = <R = never>({
25
+ toolkit: extraToolkit = OpaqueToolkit.empty,
26
+ }: { toolkit?: OpaqueToolkit.OpaqueToolkit<never, never, R> } = {}): Layer.Layer<
27
+ ToolResolverService,
28
+ never,
29
+ OpaqueToolkit.OpaqueToolkitProvider | OperationRegistry.Service | R
30
+ > => {
31
+ return Layer.effect(
32
+ ToolResolverService,
33
+ Effect.gen(function* () {
34
+ const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;
35
+ const operationRegistry = yield* OperationRegistry.Service;
36
+ return {
37
+ resolve: (id): Effect.Effect<Tool.Any, AiToolNotFoundError> =>
38
+ Effect.gen(function* () {
39
+ const toolkit = OpaqueToolkit.merge(extraToolkit, toolkitProvider.getToolkit());
40
+
41
+ const tool = toolkit.toolkit.tools[id];
42
+ if (tool) {
43
+ return tool;
44
+ }
45
+
46
+ return yield* operationRegistry.resolve(id).pipe(
47
+ Effect.flatMap(
48
+ Option.match({
49
+ onSome: (_) => Effect.succeed(projectFunctionToTool(_)),
50
+ onNone: () => Effect.fail(new AiToolNotFoundError(id)),
51
+ }),
52
+ ),
53
+ );
54
+ }),
55
+ } satisfies Context.Tag.Service<ToolResolverService>;
30
56
  }),
31
- });
57
+ );
32
58
  };
33
59
 
34
- export const makeToolExecutionServiceFromFunctions = (
35
- functions: FunctionDefinition<any, any>[],
36
- toolkit: AiToolkit.AiToolkit<AiTool.Any>,
37
- handlersLayer: Layer.Layer<AiTool.ToHandler<AiTool.AiTool<any>>, never, never>,
38
- ): Layer.Layer<ToolExecutionService, never, LocalFunctionExecutionService> => {
39
- return Layer.effect(
60
+ export const makeToolExecutionService = <E, R>(opts: {
61
+ invoke: (tool: Tool.Any, input: unknown) => Effect.Effect<unknown>;
62
+ }): Layer.Layer<ToolExecutionService, never, OpaqueToolkit.OpaqueToolkitProvider> =>
63
+ Layer.effect(
40
64
  ToolExecutionService,
41
65
  Effect.gen(function* () {
42
- const toolkitHandler = yield* toolkit.pipe(Effect.provide(handlersLayer));
43
- const localFunctionExecutionService = yield* LocalFunctionExecutionService;
66
+ const toolkitProvider = yield* OpaqueToolkit.OpaqueToolkitProvider;
67
+ const toolkit = toolkitProvider.getToolkit();
68
+
69
+ const toolkitHandler = yield* toolkit.toolkit.pipe(Effect.provide(toolkit.layer));
70
+ invariant(isHandlerLike(toolkitHandler));
44
71
 
45
72
  return {
46
73
  handlersFor: (toolkit) => {
47
- const makeHandler = (tool: AiTool.Any): ((params: unknown) => Effect.Effect<unknown, any, any>) => {
48
- return Effect.fn('toolFunctionHandler')(function* (input: any) {
49
- if (toolkitHandler.tools.find((t: AiTool.Any) => t.name === tool.name)) {
50
- // TODO(wittjosiah): Everything is `never` here.
51
- return yield* (toolkitHandler.handle as any)(tool.name, input);
52
- }
74
+ const makeHandler = (tool: Tool.Any): ((params: unknown) => Effect.Effect<unknown, any, any>) => {
75
+ return Effect.fn(`toolFunctionHandler ${tool.name}`)(function* (input: any) {
76
+ if (toolkitHandler.tools[tool.name]) {
77
+ if (Tool.isProviderDefined(tool)) {
78
+ throw new Error('Attempted to call a provider-defined tool');
79
+ }
53
80
 
54
- const { functionName } = Context.get(FunctionToolAnnotation)(tool.annotations as any);
55
- const functionDef = functions.find((fn) => fn.name === functionName);
56
- if (!functionDef) {
57
- return yield* Effect.fail(new AiToolNotFoundError(tool.name));
81
+ // TODO(wittjosiah): Everything is `never` here.
82
+ const { result } = yield* (toolkitHandler.handle as any)(tool.name, input);
83
+ return result;
58
84
  }
59
85
 
60
- return yield* localFunctionExecutionService
61
- .invokeFunction(functionDef, input)
62
- .pipe(Effect.catchAllDefect((defect) => Effect.fail(defect)));
86
+ return yield* opts.invoke(tool, input).pipe(Effect.catchAllDefect((defect) => Effect.fail(defect)));
63
87
  });
64
88
  };
65
89
 
66
- return toolkit.of(Record.map(toolkit.tools, (tool, _name) => makeHandler(tool)) as any) as any;
90
+ return toolkit.of(
91
+ Record.map(toolkit.tools, (tool, _name) => (Tool.isUserDefined(tool) ? makeHandler(tool) : null)) as any,
92
+ ) as any;
67
93
  },
68
94
  };
69
95
  }),
70
96
  );
97
+
98
+ export const makeToolExecutionServiceFromOperationInvoker = (): Layer.Layer<
99
+ ToolExecutionService,
100
+ never,
101
+ Operation.Service | OpaqueToolkit.OpaqueToolkitProvider
102
+ > => {
103
+ return Layer.unwrapEffect(
104
+ Effect.gen(function* () {
105
+ const operationInvoker = yield* Operation.Service;
106
+
107
+ return makeToolExecutionService({
108
+ invoke: (tool, input) =>
109
+ Effect.gen(function* () {
110
+ const operationDef = getOperationFromTool(tool).pipe(Option.getOrThrow);
111
+
112
+ return yield* operationInvoker.invoke(operationDef, input).pipe(Effect.orDie);
113
+ }),
114
+ });
115
+ }),
116
+ );
71
117
  };
72
118
 
73
- /**
74
- * Tools that are projected from functions have this annotation.
75
- */
76
- class FunctionToolAnnotation extends Context.Tag('@dxos/assisatnt/FunctionToolAnnotation')<
119
+ export const ToolExecutionServices = Layer.mergeAll(
120
+ makeToolResolverFromOperations(),
121
+ makeToolExecutionServiceFromOperationInvoker(),
122
+ );
123
+
124
+ class FunctionToolAnnotation extends Context.Tag('@dxos/assistant/FunctionToolAnnotation')<
77
125
  FunctionToolAnnotation,
78
- {
79
- functionName: string;
80
- }
126
+ { definition: Operation.Definition.Any }
81
127
  >() {}
82
128
 
83
- const toolCache = new WeakMap<FunctionDefinition<any, any>, AiTool.Any>();
129
+ export const getOperationFromTool = (tool: Tool.Any): Option.Option<Operation.Definition.Any> => {
130
+ return Context.getOption(FunctionToolAnnotation)(tool.annotations).pipe(Option.map(({ definition }) => definition));
131
+ };
132
+
133
+ const toolCache = new WeakMap<Operation.Definition.Any, Tool.Any>();
84
134
 
85
- const projectFunctionToTool = (fn: FunctionDefinition<any, any>): AiTool.Any => {
135
+ /**
136
+ * Projects an `Operation.Definition` into an `AiTool`.
137
+ */
138
+ const projectFunctionToTool = (fn: Operation.Definition.Any): Tool.Any => {
86
139
  if (toolCache.has(fn)) {
87
140
  return toolCache.get(fn)!;
88
141
  }
89
142
 
90
- const tool = AiTool.make(makeToolName(fn.name), {
91
- description: fn.description,
92
- parameters: createStructFieldsFromSchema(fn.inputSchema),
93
- // TODO(dmaretskyi): Include output schema.
94
- failure: Schema.Any, // TODO(dmaretskyi): Better type for the failure?
95
- }).annotate(FunctionToolAnnotation, { functionName: fn.name });
143
+ const tool = Tool.make(makeToolName(fn.meta.name ?? fn.meta.key), {
144
+ description: fn.meta.description,
145
+ parameters: createStructFieldsFromSchema(fn.input),
146
+ failure: Schema.Any,
147
+ }).annotate(FunctionToolAnnotation, { definition: fn });
96
148
  toolCache.set(fn, tool);
97
149
  return tool;
98
150
  };
@@ -101,8 +153,8 @@ const projectFunctionToTool = (fn: FunctionDefinition<any, any>): AiTool.Any =>
101
153
  * @returns Tool name produced from function name by escaping invalid characters.
102
154
  */
103
155
  const makeToolName = (name: string) => {
104
- const toolName = name.replace(/[^a-zA-Z0-9]/g, '_');
105
- invariant(toolName.match(/^[a-zA-Z_][a-zA-Z0-9-_]*$/));
156
+ const toolName = name.toLowerCase().replace(/[^a-zA-Z0-9]/g, '-');
157
+ invariant(toolName.match(/^[a-z_][a-z0-9-_]*$/));
106
158
  return toolName;
107
159
  };
108
160
 
@@ -110,10 +162,53 @@ const makeToolName = (name: string) => {
110
162
  const createStructFieldsFromSchema = (schema: Schema.Schema<any, any>): Record<string, Schema.Schema<any, any>> => {
111
163
  switch (schema.ast._tag) {
112
164
  case 'TypeLiteral':
113
- return Object.fromEntries(schema.ast.propertySignatures.map((prop) => [prop.name, Schema.make(prop.type)]));
165
+ return Object.fromEntries(
166
+ schema.ast.propertySignatures.map((prop) => [prop.name, Schema.make(mapSchemaTypeForLLM(prop.type))]),
167
+ );
114
168
  case 'VoidKeyword':
115
169
  return {};
116
170
  default:
117
171
  return todo(`Unsupported schema AST: ${schema.ast._tag}`);
118
172
  }
119
173
  };
174
+
175
+ /**
176
+ * Picks an LLM-friendly schema type for the given schema AST.
177
+ * The picked schema type decodes to the original schema type.
178
+ */
179
+ const mapSchemaTypeForLLM = (ast: SchemaAST.AST): SchemaAST.AST => {
180
+ if (Ref.isRefType(ast)) {
181
+ const description = ast.annotations.description
182
+ ? ast.annotations.description + '\n' + RefFromLLM.ast.annotations.description
183
+ : (RefFromLLM.ast.annotations.description as string);
184
+ return RefFromLLM.annotations({ description }).ast;
185
+ } else if (SchemaAST.isTupleType(ast)) {
186
+ return new SchemaAST.TupleType(
187
+ ast.elements.map((t) => new SchemaAST.OptionalType(mapSchemaTypeForLLM(t.type), t.isOptional, t.annotations)),
188
+ ast.rest.map((t) => new SchemaAST.Type(mapSchemaTypeForLLM(t.type), t.annotations)),
189
+ ast.isReadonly,
190
+ ast.annotations,
191
+ );
192
+ } else if (SchemaAST.isTypeLiteral(ast)) {
193
+ return new SchemaAST.TypeLiteral(
194
+ ast.propertySignatures.map(
195
+ (p) =>
196
+ new SchemaAST.PropertySignature(
197
+ p.name,
198
+ mapSchemaTypeForLLM(p.type),
199
+ p.isOptional,
200
+ p.isReadonly,
201
+ p.annotations,
202
+ ),
203
+ ),
204
+ ast.indexSignatures,
205
+ ast.annotations,
206
+ );
207
+ }
208
+
209
+ return ast;
210
+ };
211
+
212
+ const isHandlerLike = (value: unknown): value is Toolkit.WithHandler<Record<string, Tool.Any>> => {
213
+ return typeof (value as any).tools === 'object' && typeof (value as any).handle === 'function';
214
+ };
package/src/index.ts CHANGED
@@ -4,8 +4,10 @@
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';
13
+ export * from './tracing';
@@ -2,10 +2,10 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Context } from 'effect';
5
+ import * as Context from 'effect/Context';
6
6
 
7
7
  import { type ObjectVersion } from '@dxos/echo-db';
8
- import { type ObjectId } from '@dxos/echo-schema';
8
+ import { type ObjectId } from '@dxos/keys';
9
9
 
10
10
  /**
11
11
  * Resolves artifact ids to their versions.
@@ -2,11 +2,12 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { AiError } from '@effect/ai';
6
- import { HttpClientError } from '@effect/platform';
7
- import { Effect, Schema } from 'effect';
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
- import { AnthropicError } from '../errors';
10
+ import { AiModelError } from '../errors';
10
11
 
11
12
  const AnthropicErrorResponse = Schema.Struct({
12
13
  type: Schema.Literal('error'),
@@ -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;
@@ -24,8 +26,11 @@ export const mapAiError = (err: AiError.AiError): Effect.Effect<AiError.AiError>
24
26
  Effect.flatMap(Schema.decodeUnknown(AnthropicErrorResponse, { exact: false })),
25
27
  );
26
28
 
27
- const parsedCause = new AnthropicError(`${body.error.type}: ${body.error.message}`);
28
- return new AiError.AiError({
29
+ const parsedCause = new AiModelError({
30
+ message: body.error.message,
31
+ context: { model: 'anthropic', type: body.error.type },
32
+ });
33
+ return AiError.UnknownError.make({
29
34
  description: body.error.message,
30
35
  module: err.module,
31
36
  method: err.method,
@@ -2,21 +2,22 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Array, Effect, Option, pipe } from 'effect';
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Function from 'effect/Function';
7
+ import * as Option from 'effect/Option';
6
8
 
7
- import { Template } from '@dxos/blueprints';
8
- import { Obj } from '@dxos/echo';
9
+ import { type FunctionNotFoundError, type Operation, type OperationRegistry, Template } from '@dxos/compute';
10
+ import { type Database, Obj } from '@dxos/echo';
9
11
  import { ObjectVersion } from '@dxos/echo-db';
10
- import { type ObjectId } from '@dxos/echo-schema';
11
- import { DatabaseService } from '@dxos/functions';
12
+ import type { ObjectNotFoundError } from '@dxos/echo/Err';
13
+ import { type ObjectId } from '@dxos/keys';
12
14
  import { log } from '@dxos/log';
13
- import { type ContentBlock, DataType } from '@dxos/schema';
15
+ import { type ContentBlock, Message } from '@dxos/types';
14
16
  import { trim } from '@dxos/util';
15
17
 
16
18
  import { AiAssistantError } from '../errors';
17
-
18
19
  import { ArtifactDiffResolver } from './artifact-diff';
19
- import { type AiSessionRunParams } from './session';
20
+ import { type AiRequestRunError, type AiRequestRunProps } from './request';
20
21
 
21
22
  /**
22
23
  * Formats the system prompt.
@@ -26,20 +27,24 @@ export const formatSystemPrompt = ({
26
27
  system,
27
28
  blueprints = [],
28
29
  objects = [],
29
- }: Pick<AiSessionRunParams<any>, 'system' | 'blueprints' | 'objects'>) =>
30
+ }: Pick<AiRequestRunProps, 'system' | 'blueprints' | 'objects'>): Effect.Effect<
31
+ string,
32
+ FunctionNotFoundError | ObjectNotFoundError,
33
+ Database.Service | OperationRegistry.Service | Operation.Service
34
+ > =>
30
35
  Effect.gen(function* () {
31
- const blueprintDefs = yield* pipe(
36
+ const blueprintDefs = yield* Function.pipe(
32
37
  blueprints,
33
38
  Effect.forEach((blueprint) => Effect.succeed(blueprint.instructions)),
34
- Effect.flatMap(Effect.forEach((template) => DatabaseService.loadOption(template.source))),
35
- Effect.map(Array.filter(Option.isSome)),
36
- Effect.map(
37
- Array.map(
38
- (template) => trim`
39
+ Effect.flatMap(
40
+ Effect.forEach((template) =>
41
+ Effect.gen(function* () {
42
+ return trim`
39
43
  <blueprint>
40
- ${Template.process(template.value.content)}
44
+ ${yield* Template.processTemplate(template)}
41
45
  </blueprint>
42
- `,
46
+ `;
47
+ }),
43
48
  ),
44
49
  ),
45
50
  Effect.map((blueprints) =>
@@ -47,7 +52,7 @@ export const formatSystemPrompt = ({
47
52
  ),
48
53
  );
49
54
 
50
- const objectDefs = yield* pipe(
55
+ const objectDefs = yield* Function.pipe(
51
56
  objects,
52
57
  Effect.forEach((object) =>
53
58
  Effect.succeed(trim`
@@ -60,18 +65,21 @@ export const formatSystemPrompt = ({
60
65
  Effect.map((objects) => (objects.length > 0 ? ['## Context Objects', ...objects].join('\n\n') : undefined)),
61
66
  );
62
67
 
63
- return yield* pipe(
68
+ return yield* Function.pipe(
64
69
  Effect.succeed([system, blueprintDefs, objectDefs].filter((def): def is string => def !== undefined)),
65
70
  Effect.map((parts) => parts.join('\n\n')),
66
71
  );
67
- });
72
+ }).pipe(Effect.withSpan('formatSystemPrompt'));
68
73
 
69
74
  /**
70
75
  * Formats the user prompt.
71
76
  */
72
77
  // TODO(burdon): Move to AiPreprocessor.
73
78
  // TODO(burdon): Convert util below to `Effect.fn` (to preserve stack info)
74
- export const formatUserPrompt = ({ prompt, history = [] }: Pick<AiSessionRunParams<any>, 'prompt' | 'history'>) =>
79
+ export const formatUserPrompt = ({
80
+ prompt,
81
+ history = [],
82
+ }: Pick<AiRequestRunProps, 'prompt' | 'history'>): Effect.Effect<Message.Message, AiRequestRunError> =>
75
83
  Effect.gen(function* () {
76
84
  const blocks: ContentBlock.Any[] = [];
77
85
 
@@ -84,7 +92,7 @@ export const formatUserPrompt = ({ prompt, history = [] }: Pick<AiSessionRunPara
84
92
  artifactDiffResolver.value.resolve(
85
93
  [...versions.entries()].map(([id, version]) => ({ id, lastVersion: version })),
86
94
  ),
87
- catch: AiAssistantError.wrap('Artifact diff resolution error'),
95
+ catch: AiAssistantError.wrap({ message: 'Artifact diff resolution error' }),
88
96
  });
89
97
 
90
98
  log('version', { artifactDiff, versions });
@@ -102,14 +110,14 @@ export const formatUserPrompt = ({ prompt, history = [] }: Pick<AiSessionRunPara
102
110
  }
103
111
  }
104
112
 
105
- return Obj.make(DataType.Message, {
113
+ return Obj.make(Message.Message, {
106
114
  created: new Date().toISOString(),
107
115
  sender: { role: 'user' },
108
- blocks: [...blocks, { _tag: 'text', text: prompt }],
116
+ blocks: typeof prompt === 'string' ? [...blocks, { _tag: 'text', text: prompt }] : [...blocks, ...prompt],
109
117
  });
110
- });
118
+ }).pipe(Effect.withSpan('formatUserPrompt'));
111
119
 
112
- const gatherObjectVersions = (messages: DataType.Message[]): Map<ObjectId, ObjectVersion> => {
120
+ const gatherObjectVersions = (messages: Message.Message[]): Map<ObjectId, ObjectVersion> => {
113
121
  const artifactIds = new Map<ObjectId, ObjectVersion>();
114
122
  for (const message of messages) {
115
123
  for (const block of message.blocks) {
@@ -128,7 +136,7 @@ const createArtifactUpdateBlock = (
128
136
  return {
129
137
  _tag: 'text',
130
138
  // TODO(dmaretskyi): Does this need to be a special content-block?
131
- disposition: 'artifact-update',
139
+ disposition: 'synthetic',
132
140
  text: trim`
133
141
  The following artifacts have been updated since the last message:
134
142
  ${[...artifactDiff.entries()]
@@ -4,6 +4,7 @@
4
4
 
5
5
  export * from './artifact-diff';
6
6
  export * from './observer';
7
- export * from './session';
7
+ export * from './request';
8
8
  export * from './toolkit';
9
9
  export * from './version-pin';
10
+ export * from './format';
@@ -2,20 +2,24 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Effect, Function } from 'effect';
5
+ import type * as Tool from '@effect/ai/Tool';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Function from 'effect/Function';
6
8
 
7
9
  import { type AiParser, type ConsolePrinter } from '@dxos/ai';
8
- import { type DataType } from '@dxos/schema';
10
+ import { type Message } from '@dxos/types';
9
11
 
10
12
  /**
11
13
  * Live observer of the generation process.
12
14
  */
13
- export interface GenerationObserver extends AiParser.ParseResponseCallbacks {
15
+ export interface GenerationObserver<
16
+ Tools extends Record<string, Tool.Any> = any,
17
+ > extends AiParser.ParseResponseCallbacks<Tools> {
14
18
  /**
15
19
  * Complete messages fired during the session, both from the model and from the user.
16
20
  * This message will contain all message blocks emitted through the `onBlock` callback.
17
21
  */
18
- onMessage: (message: DataType.Message) => Effect.Effect<void>;
22
+ onMessage: (message: Message.Message) => Effect.Effect<void>;
19
23
  }
20
24
 
21
25
  export const GenerationObserver = Object.freeze({