@dxos/assistant 0.8.4-main.e8ec1fe → 0.8.4-main.ef1bc66f44

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 (100) hide show
  1. package/dist/lib/neutral/chunk-ZPC54VRM.mjs +596 -0
  2. package/dist/lib/neutral/chunk-ZPC54VRM.mjs.map +7 -0
  3. package/dist/lib/{browser → neutral}/extraction/index.mjs +5 -5
  4. package/dist/lib/neutral/extraction/index.mjs.map +7 -0
  5. package/dist/lib/{browser → neutral}/index.mjs +320 -681
  6. package/dist/lib/neutral/index.mjs.map +7 -0
  7. package/dist/lib/neutral/meta.json +1 -0
  8. package/dist/lib/neutral/testing/index.mjs +38 -0
  9. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  10. package/dist/types/src/conversation/context.d.ts +97 -62
  11. package/dist/types/src/conversation/context.d.ts.map +1 -1
  12. package/dist/types/src/conversation/context.test.d.ts +2 -0
  13. package/dist/types/src/conversation/context.test.d.ts.map +1 -0
  14. package/dist/types/src/conversation/conversation.d.ts +40 -15
  15. package/dist/types/src/conversation/conversation.d.ts.map +1 -1
  16. package/dist/types/src/conversation/conversation.test.d.ts +2 -0
  17. package/dist/types/src/conversation/conversation.test.d.ts.map +1 -0
  18. package/dist/types/src/diff.d.ts +3 -1
  19. package/dist/types/src/diff.d.ts.map +1 -1
  20. package/dist/types/src/errors.d.ts +12 -16
  21. package/dist/types/src/errors.d.ts.map +1 -1
  22. package/dist/types/src/execution-graph.d.ts +2 -2
  23. package/dist/types/src/execution-graph.d.ts.map +1 -1
  24. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +247 -245
  25. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +1 -1
  26. package/dist/types/src/extraction/extraction.d.ts +342 -308
  27. package/dist/types/src/extraction/extraction.d.ts.map +1 -1
  28. package/dist/types/src/extraction/quotes.d.ts +2 -2
  29. package/dist/types/src/extraction/quotes.d.ts.map +1 -1
  30. package/dist/types/src/functions/services.d.ts +6 -7
  31. package/dist/types/src/functions/services.d.ts.map +1 -1
  32. package/dist/types/src/functions.test.d.ts +2 -0
  33. package/dist/types/src/functions.test.d.ts.map +1 -0
  34. package/dist/types/src/session/GenericToolkit.d.ts +23 -0
  35. package/dist/types/src/session/GenericToolkit.d.ts.map +1 -1
  36. package/dist/types/src/session/artifact-diff.d.ts +1 -1
  37. package/dist/types/src/session/artifact-diff.d.ts.map +1 -1
  38. package/dist/types/src/session/format.d.ts +3 -3
  39. package/dist/types/src/session/format.d.ts.map +1 -1
  40. package/dist/types/src/session/index.d.ts +1 -0
  41. package/dist/types/src/session/index.d.ts.map +1 -1
  42. package/dist/types/src/session/observer.d.ts +4 -3
  43. package/dist/types/src/session/observer.d.ts.map +1 -1
  44. package/dist/types/src/session/session.d.ts +14 -9
  45. package/dist/types/src/session/session.d.ts.map +1 -1
  46. package/dist/types/src/session/toolkit.d.ts +5 -6
  47. package/dist/types/src/session/toolkit.d.ts.map +1 -1
  48. package/dist/types/src/session/version-pin.d.ts +2 -2
  49. package/dist/types/src/session/version-pin.d.ts.map +1 -1
  50. package/dist/types/src/testing/index.d.ts +2 -0
  51. package/dist/types/src/testing/index.d.ts.map +1 -0
  52. package/dist/types/src/testing/layer.d.ts +22 -0
  53. package/dist/types/src/testing/layer.d.ts.map +1 -0
  54. package/dist/types/src/types/artifact.d.ts +13 -5
  55. package/dist/types/src/types/artifact.d.ts.map +1 -1
  56. package/dist/types/tsconfig.tsbuildinfo +1 -1
  57. package/package.json +40 -33
  58. package/src/conversation/context.test.ts +46 -0
  59. package/src/conversation/context.ts +211 -67
  60. package/src/conversation/conversation.test.ts +65 -0
  61. package/src/conversation/conversation.ts +123 -48
  62. package/src/diff.test.ts +35 -4
  63. package/src/diff.ts +13 -4
  64. package/src/errors.ts +2 -2
  65. package/src/execution-graph.test.ts +1 -1
  66. package/src/execution-graph.ts +4 -4
  67. package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +7 -7
  68. package/src/extraction/extraction-ner-function/extraction-ner-function.ts +1 -1
  69. package/src/extraction/extraction.ts +4 -4
  70. package/src/extraction/quotes.ts +4 -5
  71. package/src/functions/services.ts +55 -30
  72. package/src/functions.conversations.json +1 -0
  73. package/src/functions.test.ts +74 -0
  74. package/src/session/GenericToolkit.ts +30 -0
  75. package/src/session/artifact-diff.ts +1 -1
  76. package/src/session/format.test.ts +101 -38
  77. package/src/session/format.ts +12 -13
  78. package/src/session/index.ts +1 -0
  79. package/src/session/observer.ts +3 -1
  80. package/src/session/session.conversations.json +1 -1
  81. package/src/session/session.test.ts +26 -13
  82. package/src/session/session.ts +36 -14
  83. package/src/session/toolkit.ts +13 -22
  84. package/src/session/version-pin.ts +4 -3
  85. package/src/templates/instructions/format.tpl +0 -4
  86. package/src/testing/index.ts +5 -0
  87. package/src/testing/layer.ts +84 -0
  88. package/src/types/artifact.ts +31 -13
  89. package/dist/lib/browser/extraction/index.mjs.map +0 -7
  90. package/dist/lib/browser/index.mjs.map +0 -7
  91. package/dist/lib/browser/meta.json +0 -1
  92. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  93. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  94. package/dist/lib/node-esm/extraction/index.mjs +0 -226
  95. package/dist/lib/node-esm/extraction/index.mjs.map +0 -7
  96. package/dist/lib/node-esm/index.mjs +0 -1291
  97. package/dist/lib/node-esm/index.mjs.map +0 -7
  98. package/dist/lib/node-esm/meta.json +0 -1
  99. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  100. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
@@ -0,0 +1 @@
1
+ {"conversations":[{"parameters":{"model":"@anthropic/claude-opus-4-0","stream":true,"tools":[{"name":"read-name","description":"Reads the name of an organization.","inputSchema":{"type":"object","required":["org"],"properties":{"org":{"type":"string","description":"The ID of the referenced object. Formats accepted:\n- DXN (dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4). DXNs can be prepended with an @ symbol for compatibility with in-text references.\n- space ID, object ID tuple (spaceID:objectID)\n- Only object ID that is assumed to be in the current space (01KG7R1ZXWFMWQ4DA1Q6TN1DG4)","examples":["dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4","@dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4","BM3FSHFOMJCHCG5QW7JTVKGYABD2GAA7G:01KG7R1ZXWFMWQ4DA1Q6TN1DG4","01KG7R1ZXWFMWQ4DA1Q6TN1DG4"]}},"additionalProperties":false}}]},"prompt":{"content":[{"role":"user","content":"What is the name of the organization? 01JGFJJZ00G0WKQSJGMAKCNT89","options":{"anthropic":{"cacheControl":{"ttl":"5m","type":"ephemeral"}}}}]},"response":[{"type":"response-metadata","id":"msg_01FqHQGxFVdQKeMXjodHavsF","modelId":"claude-opus-4-20250514","timestamp":"1970-01-01T00:00:00.000Z","metadata":{}},{"type":"reasoning-start","id":"0","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"The user is asking for the name of","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" an organization an","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"d has provided what","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" appears to be an","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" object","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" ID: 01JGFJ","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"JZ00G0WK","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"QSJGMAKCNT89","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"\n\nI","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" need to use the read-name function","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" to get the name of this organization.","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" According","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" to the function description, I can","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" pass","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" just","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" the object","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" ID if","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" it's assume","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"d to be in the current space.","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"\n\nLet me call","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" the function with this","metadata":{}},{"type":"reasoning-delta","id":"0","delta":" ID.","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"","metadata":{}},{"type":"reasoning-delta","id":"0","delta":"","metadata":{"anthropic":{"type":"thinking","signature":"EokECkYICxgCKkDkOVtqWk3+YJ1nk31yy/hYUygSTbpQxoD7UKl2RqwyS+tJSV2rou35JEFIl30ogJZiohCBXqY0IZ3YO1SwhWq1EgztHHPI5rGjH4gehN8aDBh3BbEfM6OCraoAQiIwDSGtzFCbT7K+yTBtmtri6uuGvPwNGmRshKURPnFRCjdAf0gj1Jwge4pmIYnlGpRGKvACF4kjtsTHsn8f7ktuuzSEpVNaC6d5VYA6/Fm5ZFaiQPn0/F4fQXy1PNEyo4007gP21Ate8+98CCtlfkOnEE53hElq58doXrVOzpL+uH5b6PBPtM9f1+L9cKjhTPV6djIcewwHTTSJDTM6fr9c82f9u5EDvzNe1QODqlqCiVtNsjWCYOWNZUqILdl0TQ43Hzjf2T+WN6ARHfd3mGVsAtg8vcBM8D4mPvThsTnoswYIeB6Y5aDAA+Xu32jGRmJGclevuy25lJFZG8lAhOXJOdiP88zTG5XCN5ED3GLo2rjKBnQzwGpzg1M0ARKkrNzuLbNZ31uk36ayo+1T381Ld0yIQAX/ZUrLNZ8ovJ1sIHW18hmnyec69+4qWuy//NBWlAfyFOQnQfXM1f2XO5AFBrSILGtHxWM1+KINm1Xh68gHZpjjpyM5g8zl61nDLGxNq3OBugM7NSghygmGPmy2DlxU147cVzOHh7ZXh8fFxBfZlW0YAQ=="}}},{"type":"reasoning-end","id":"0","metadata":{}},{"type":"text-start","id":"1","metadata":{}},{"type":"text-delta","id":"1","delta":"I","metadata":{}},{"type":"text-delta","id":"1","delta":"'ll look","metadata":{}},{"type":"text-delta","id":"1","delta":" up the name of that","metadata":{}},{"type":"text-delta","id":"1","delta":" organization for you.","metadata":{}},{"type":"text-end","id":"1","metadata":{}},{"type":"tool-params-start","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","name":"read-name","providerExecuted":false,"metadata":{}},{"type":"tool-params-delta","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","delta":"","metadata":{}},{"type":"tool-params-delta","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","delta":"{\"or","metadata":{}},{"type":"tool-params-delta","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","delta":"g\": \"01JGFJJ","metadata":{}},{"type":"tool-params-delta","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","delta":"Z00G0WKQ","metadata":{}},{"type":"tool-params-delta","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","delta":"SJGM","metadata":{}},{"type":"tool-params-delta","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","delta":"AKCNT89\"}","metadata":{}},{"type":"tool-params-end","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","metadata":{}},{"type":"tool-call","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","name":"read-name","providerExecuted":false,"metadata":{},"params":{"org":"dxn:echo:@:01JGFJJZ00G0WKQSJGMAKCNT89"}},{"type":"finish","reason":"tool-calls","usage":{"inputTokens":729,"outputTokens":187,"totalTokens":916},"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":729,"output_tokens":8,"server_tool_use":null,"service_tier":"standard"}}}}]},{"parameters":{"model":"@anthropic/claude-opus-4-0","stream":true,"tools":[{"name":"read-name","description":"Reads the name of an organization.","inputSchema":{"type":"object","required":["org"],"properties":{"org":{"type":"string","description":"The ID of the referenced object. Formats accepted:\n- DXN (dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4). DXNs can be prepended with an @ symbol for compatibility with in-text references.\n- space ID, object ID tuple (spaceID:objectID)\n- Only object ID that is assumed to be in the current space (01KG7R1ZXWFMWQ4DA1Q6TN1DG4)","examples":["dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4","@dxn:echo:@:01KG7R1ZXWFMWQ4DA1Q6TN1DG4","BM3FSHFOMJCHCG5QW7JTVKGYABD2GAA7G:01KG7R1ZXWFMWQ4DA1Q6TN1DG4","01KG7R1ZXWFMWQ4DA1Q6TN1DG4"]}},"additionalProperties":false}}]},"prompt":{"content":[{"role":"user","content":"What is the name of the organization? 01JGFJJZ00G0WKQSJGMAKCNT89","options":{}},{"role":"assistant","content":[{"type":"reasoning","text":"The user is asking for the name of an organization and has provided what appears to be an object ID: 01JGFJJZ00G0WKQSJGMAKCNT89\n\nI need to use the read-name function to get the name of this organization. According to the function description, I can pass just the object ID if it's assumed to be in the current space.\n\nLet me call the function with this ID.","options":{}},{"type":"text","text":"I'll look up the name of that organization for you.","options":{}},{"type":"tool-call","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","name":"read-name","params":{"org":"01JGFJJZ00G0WKQSJGMAKCNT89"},"providerExecuted":false,"options":{}}],"options":{}},{"role":"tool","content":[{"type":"tool-result","id":"toolu_01DdHbHNejZJC4CEjngmNLCf","name":"read-name","isFailure":false,"result":"BlueYard","providerExecuted":false,"options":{}}],"options":{"anthropic":{"cacheControl":{"ttl":"5m","type":"ephemeral"}}}}]},"response":[{"type":"response-metadata","id":"msg_01X5xvtDymPG7tTUMLZz3eAD","modelId":"claude-opus-4-20250514","timestamp":"1970-01-01T00:00:00.000Z","metadata":{}},{"type":"text-start","id":"0","metadata":{}},{"type":"text-delta","id":"0","delta":"The name of the organization with","metadata":{}},{"type":"text-delta","id":"0","delta":" ID 01JGFJJ","metadata":{}},{"type":"text-delta","id":"0","delta":"Z00G0WKQ","metadata":{}},{"type":"text-delta","id":"0","delta":"SJGMAKCNT89 is","metadata":{}},{"type":"text-delta","id":"0","delta":" **BlueYard**.","metadata":{}},{"type":"text-end","id":"0","metadata":{}},{"type":"finish","reason":"stop","usage":{"inputTokens":801,"outputTokens":39,"totalTokens":840},"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":801,"output_tokens":6,"server_tool_use":null,"service_tier":"standard"}}}}]}]}
@@ -0,0 +1,74 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { describe, it } from '@effect/vitest';
6
+ import * as Effect from 'effect/Effect';
7
+ import * as Schema from 'effect/Schema';
8
+
9
+ import { ConsolePrinter } from '@dxos/ai';
10
+ import { MemoizedAiService } from '@dxos/ai/testing';
11
+ import { AiSession, GenerationObserver, createToolkit } from '@dxos/assistant';
12
+ import { Blueprint } from '@dxos/blueprints';
13
+ import { Database, Obj, Type } from '@dxos/echo';
14
+ import { TestHelpers } from '@dxos/effect/testing';
15
+ import { defineFunction } from '@dxos/functions';
16
+ import { ObjectId } from '@dxos/keys';
17
+ import { Organization } from '@dxos/types';
18
+
19
+ import { AssistantTestLayer } from './testing';
20
+
21
+ ObjectId.dangerouslyDisableRandomness();
22
+
23
+ const readName = defineFunction({
24
+ key: 'dxos.org/function/read-name',
25
+ name: 'Read Name',
26
+ description: 'Reads the name of an organization.',
27
+ inputSchema: Schema.Struct({
28
+ org: Type.Ref(Organization.Organization),
29
+ }),
30
+ outputSchema: Schema.String,
31
+ handler: Effect.fnUntraced(function* ({ data }) {
32
+ const org = yield* Database.load(data.org);
33
+ return org.name ?? '<no org>';
34
+ }),
35
+ });
36
+
37
+ const blueprint = Blueprint.make({
38
+ key: 'dxos.org/blueprint/test',
39
+ name: 'Test blueprint',
40
+ tools: Blueprint.toolDefinitions({ functions: [readName] }),
41
+ });
42
+
43
+ const TestLayer = AssistantTestLayer({
44
+ aiServicePreset: 'edge-remote',
45
+ functions: [readName],
46
+ types: [Organization.Organization],
47
+ });
48
+
49
+ describe('Research', () => {
50
+ it.effect(
51
+ 'call a function with a ref input',
52
+ Effect.fnUntraced(
53
+ function* (_) {
54
+ const org = yield* Database.add(
55
+ Obj.make(Organization.Organization, {
56
+ name: 'BlueYard',
57
+ website: 'https://blueyard.com',
58
+ }),
59
+ );
60
+ yield* Database.flush({ indexes: true });
61
+ yield* new AiSession().run({
62
+ prompt: `What is the name of the organization? ${org.id}`,
63
+ toolkit: yield* createToolkit({
64
+ blueprints: [blueprint],
65
+ }),
66
+ observer: GenerationObserver.fromPrinter(new ConsolePrinter()),
67
+ });
68
+ },
69
+ Effect.provide(TestLayer),
70
+ TestHelpers.provideTestContext,
71
+ ),
72
+ MemoizedAiService.isGenerationEnabled() ? 240_000 : 30_000,
73
+ );
74
+ });
@@ -4,6 +4,7 @@
4
4
 
5
5
  import type * as Tool from '@effect/ai/Tool';
6
6
  import * as Toolkit from '@effect/ai/Toolkit';
7
+ import * as Context from 'effect/Context';
7
8
  import * as Effect from 'effect/Effect';
8
9
  import * as Layer from 'effect/Layer';
9
10
  import * as Pipeable from 'effect/Pipeable';
@@ -83,6 +84,11 @@ export const make = <Tools extends Record<string, Tool.Any>, E, R>(
83
84
  };
84
85
  };
85
86
 
87
+ /**
88
+ * Empty generic toolkit.
89
+ */
90
+ export const empty = make(Toolkit.empty, Layer.empty);
91
+
86
92
  /**
87
93
  * Merges multiple portable toolkits into a single portable toolkit.
88
94
  */
@@ -113,6 +119,7 @@ export type GenericTools<R = never> = Record<
113
119
  readonly parameters: AnyStructSchemaNoContext;
114
120
  readonly success: Schema.Schema.AnyNoContext;
115
121
  readonly failure: typeof Schema.Never;
122
+ readonly failureMode: Tool.FailureMode;
116
123
  },
117
124
  R
118
125
  >
@@ -128,3 +135,26 @@ export interface AnyStructSchemaNoContext extends Pipeable.Pipeable {
128
135
  readonly fields: Schema.Struct.Fields;
129
136
  readonly annotations: any;
130
137
  }
138
+
139
+ /**
140
+ * Provides a generic toolkit to the agent.
141
+ */
142
+ export class Provider extends Context.Tag('@dxos/assistant/GenericToolkit.Provider')<
143
+ Provider,
144
+ {
145
+ readonly getToolkit: () => GenericToolkit;
146
+ }
147
+ >() {}
148
+
149
+ /**
150
+ * Layer for providing a generic toolkit to the agent.
151
+ */
152
+ export const providerLayer = (toolkit: GenericToolkit) =>
153
+ Layer.succeed(Provider, {
154
+ getToolkit: () => toolkit,
155
+ });
156
+
157
+ /**
158
+ * Layer for providing an empty generic toolkit to the agent.
159
+ */
160
+ export const providerEmpty = providerLayer(empty);
@@ -4,8 +4,8 @@
4
4
 
5
5
  import * as Context from 'effect/Context';
6
6
 
7
- import { type ObjectId } from '@dxos/echo/internal';
8
7
  import { type ObjectVersion } from '@dxos/echo-db';
8
+ import { type ObjectId } from '@dxos/keys';
9
9
 
10
10
  /**
11
11
  * Resolves artifact ids to their versions.
@@ -2,60 +2,123 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { beforeAll, describe, it } from '@effect/vitest';
5
+ import { describe, expect, it } from '@effect/vitest';
6
6
  import * as Effect from 'effect/Effect';
7
+ import * as Schema from 'effect/Schema';
7
8
 
8
9
  import { Blueprint, Template } from '@dxos/blueprints';
9
- import { Obj } from '@dxos/echo';
10
- import { type EchoDatabase } from '@dxos/echo-db';
11
- import { EchoTestBuilder } from '@dxos/echo-db/testing';
10
+ import { Database, Obj, Query } from '@dxos/echo';
11
+ import { TestHelpers } from '@dxos/effect/testing';
12
+ import { defineFunction } from '@dxos/functions';
12
13
  import { Text } from '@dxos/schema';
13
14
  import { Organization } from '@dxos/types';
14
15
  import { trim } from '@dxos/util';
15
16
 
16
17
  import { createSystemPrompt } from '../templates/system';
18
+ import { AssistantTestLayer } from '../testing';
17
19
 
18
20
  import { formatSystemPrompt } from './format';
19
21
 
20
- describe('format', () => {
21
- let db: EchoDatabase;
22
- beforeAll(async () => {
23
- const builder = await new EchoTestBuilder().open();
24
- ({ db } = await builder.createDatabase({
25
- types: [Text.Text, Organization.Organization, Blueprint.Blueprint],
26
- }));
27
- });
22
+ const organizationListFunction = defineFunction({
23
+ key: 'dxos.org/function/organization-list',
24
+ name: 'Organization List',
25
+ description: 'List organizations',
26
+ inputSchema: Schema.Struct({}),
27
+ outputSchema: Schema.Array(Schema.String),
28
+ handler: Effect.fnUntraced(function* () {
29
+ const organizations = yield* Database.runQuery(Query.type(Organization.Organization));
30
+ return organizations.map((organization) => organization.name);
31
+ }),
32
+ });
33
+
34
+ const TestLayer = AssistantTestLayer({
35
+ types: [Text.Text, Organization.Organization, Blueprint.Blueprint],
36
+ functions: [organizationListFunction],
37
+ });
28
38
 
39
+ describe('format', () => {
29
40
  it.effect(
30
41
  'should format',
31
- Effect.fn(function* () {
32
- const object = db.add(
33
- Obj.make(Organization.Organization, {
34
- name: 'Test',
35
- website: 'https://www.test.com',
36
- }),
37
- );
38
-
39
- const blueprint = db.add(
40
- Blueprint.make({
41
- key: 'example.com/blueprint/test',
42
- name: 'Test',
43
- instructions: Template.make({
44
- source: trim`
45
- Test
46
- This is the test blueprint.
47
- `,
42
+ Effect.fnUntraced(
43
+ function* (_) {
44
+ const { db } = yield* Database.Service;
45
+ const object = db.add(
46
+ Obj.make(Organization.Organization, {
47
+ name: 'Test',
48
+ website: 'https://www.test.com',
49
+ }),
50
+ );
51
+
52
+ const blueprint = db.add(
53
+ Blueprint.make({
54
+ key: 'example.com/blueprint/test',
55
+ name: 'Test',
56
+ instructions: Template.make({
57
+ source: trim`
58
+ Test
59
+ This is the test blueprint.
60
+ `,
61
+ }),
62
+ }),
63
+ );
64
+
65
+ const output = yield* formatSystemPrompt({
66
+ system: createSystemPrompt({}),
67
+ blueprints: [blueprint],
68
+ objects: [object],
69
+ });
70
+
71
+ console.log(output);
72
+ },
73
+ Effect.provide(TestLayer),
74
+ TestHelpers.provideTestContext,
75
+ ),
76
+ );
77
+
78
+ it.effect(
79
+ 'should format with function input',
80
+ Effect.fnUntraced(
81
+ function* (_) {
82
+ const { db } = yield* Database.Service;
83
+ const object = db.add(
84
+ Obj.make(Organization.Organization, {
85
+ name: 'Test Org',
86
+ website: 'https://www.test.com',
87
+ }),
88
+ );
89
+
90
+ const blueprint = db.add(
91
+ Blueprint.make({
92
+ key: 'example.com/blueprint/test',
93
+ name: 'Test',
94
+ instructions: Template.make({
95
+ source: trim`
96
+ Organization List:
97
+ {{#each organizations}}
98
+ - {{this}}
99
+ {{/each}}
100
+ `,
101
+ inputs: [
102
+ {
103
+ name: 'organizations',
104
+ kind: 'function',
105
+ function: organizationListFunction.key,
106
+ },
107
+ ],
108
+ }),
48
109
  }),
49
- }),
50
- );
110
+ );
51
111
 
52
- const output = yield* formatSystemPrompt({
53
- system: createSystemPrompt({}),
54
- blueprints: [blueprint, blueprint],
55
- objects: [object, object],
56
- });
112
+ const output = yield* formatSystemPrompt({
113
+ system: createSystemPrompt({}),
114
+ blueprints: [blueprint],
115
+ objects: [object],
116
+ });
57
117
 
58
- console.log(output);
59
- }),
118
+ expect(output).to.include('Test Org');
119
+ },
120
+ Effect.provide(TestLayer),
121
+ TestHelpers.provideTestContext,
122
+ ),
60
123
  );
61
124
  });
@@ -2,15 +2,14 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import * as Array from 'effect/Array';
6
5
  import * as Effect from 'effect/Effect';
7
6
  import * as Function from 'effect/Function';
8
7
  import * as Option from 'effect/Option';
9
8
 
10
9
  import { Template } from '@dxos/blueprints';
11
10
  import { Obj } from '@dxos/echo';
12
- import { type ObjectId } from '@dxos/echo/internal';
13
- import { DatabaseService, ObjectVersion } from '@dxos/echo-db';
11
+ import { ObjectVersion } from '@dxos/echo-db';
12
+ import { type ObjectId } from '@dxos/keys';
14
13
  import { log } from '@dxos/log';
15
14
  import { type ContentBlock, Message } from '@dxos/types';
16
15
  import { trim } from '@dxos/util';
@@ -18,7 +17,7 @@ import { trim } from '@dxos/util';
18
17
  import { AiAssistantError } from '../errors';
19
18
 
20
19
  import { ArtifactDiffResolver } from './artifact-diff';
21
- import { type AiSessionRunError, type AiSessionRunParams } from './session';
20
+ import { type AiSessionRunError, type AiSessionRunProps } from './session';
22
21
 
23
22
  /**
24
23
  * Formats the system prompt.
@@ -28,20 +27,20 @@ export const formatSystemPrompt = ({
28
27
  system,
29
28
  blueprints = [],
30
29
  objects = [],
31
- }: Pick<AiSessionRunParams<any>, 'system' | 'blueprints' | 'objects'>) =>
30
+ }: Pick<AiSessionRunProps<any>, 'system' | 'blueprints' | 'objects'>) =>
32
31
  Effect.gen(function* () {
33
32
  const blueprintDefs = yield* Function.pipe(
34
33
  blueprints,
35
34
  Effect.forEach((blueprint) => Effect.succeed(blueprint.instructions)),
36
- Effect.flatMap(Effect.forEach((template) => DatabaseService.loadOption(template.source))),
37
- Effect.map(Array.filter(Option.isSome)),
38
- Effect.map(
39
- Array.map(
40
- (template) => trim`
35
+ Effect.flatMap(
36
+ Effect.forEach((template) =>
37
+ Effect.gen(function* () {
38
+ return trim`
41
39
  <blueprint>
42
- ${Template.process(template.value.content)}
40
+ ${yield* Template.processTemplate(template)}
43
41
  </blueprint>
44
- `,
42
+ `;
43
+ }),
45
44
  ),
46
45
  ),
47
46
  Effect.map((blueprints) =>
@@ -76,7 +75,7 @@ export const formatSystemPrompt = ({
76
75
  export const formatUserPrompt = ({
77
76
  prompt,
78
77
  history = [],
79
- }: Pick<AiSessionRunParams<any>, 'prompt' | 'history'>): Effect.Effect<Message.Message, AiSessionRunError> =>
78
+ }: Pick<AiSessionRunProps<any>, 'prompt' | 'history'>): Effect.Effect<Message.Message, AiSessionRunError> =>
80
79
  Effect.gen(function* () {
81
80
  const blocks: ContentBlock.Any[] = [];
82
81
 
@@ -7,5 +7,6 @@ export * from './observer';
7
7
  export * from './session';
8
8
  export * from './toolkit';
9
9
  export * from './version-pin';
10
+ export * from './format';
10
11
 
11
12
  export * as GenericToolkit from './GenericToolkit';
@@ -2,6 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import type * as Tool from '@effect/ai/Tool';
5
6
  import * as Effect from 'effect/Effect';
6
7
  import * as Function from 'effect/Function';
7
8
 
@@ -11,7 +12,8 @@ import { type Message } from '@dxos/types';
11
12
  /**
12
13
  * Live observer of the generation process.
13
14
  */
14
- export interface GenerationObserver extends AiParser.ParseResponseCallbacks {
15
+ export interface GenerationObserver<Tools extends Record<string, Tool.Any> = any>
16
+ extends AiParser.ParseResponseCallbacks<Tools> {
15
17
  /**
16
18
  * Complete messages fired during the session, both from the model and from the user.
17
19
  * This message will contain all message blocks emitted through the `onBlock` callback.