@dxos/assistant 0.8.4-main.a4bbb77 → 0.8.4-main.abd8ff62ef

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 (147) 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 +1731 -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 +120 -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 +78 -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 +32 -40
  18. package/dist/types/src/errors.d.ts.map +1 -1
  19. package/dist/types/src/execution-graph.d.ts +5 -2
  20. package/dist/types/src/execution-graph.d.ts.map +1 -1
  21. package/dist/types/src/extraction/extraction-ner-function/index.d.ts +0 -1
  22. package/dist/types/src/extraction/extraction-ner-function/index.d.ts.map +1 -1
  23. package/dist/types/src/extraction/extraction-ner-function/named-entity-recognition.d.ts.map +1 -1
  24. package/dist/types/src/extraction/extraction.d.ts +1066 -307
  25. package/dist/types/src/extraction/extraction.d.ts.map +1 -1
  26. package/dist/types/src/extraction/index.d.ts +0 -1
  27. package/dist/types/src/extraction/index.d.ts.map +1 -1
  28. package/dist/types/src/extraction/quotes.d.ts +3 -3
  29. package/dist/types/src/extraction/quotes.d.ts.map +1 -1
  30. package/dist/types/src/functions/services.d.ts +15 -16
  31. package/dist/types/src/functions/services.d.ts.map +1 -1
  32. package/dist/types/src/index.d.ts +1 -0
  33. package/dist/types/src/index.d.ts.map +1 -1
  34. package/dist/types/src/session/artifact-diff.d.ts +2 -2
  35. package/dist/types/src/session/artifact-diff.d.ts.map +1 -1
  36. package/dist/types/src/session/error-handling.d.ts +2 -2
  37. package/dist/types/src/session/error-handling.d.ts.map +1 -1
  38. package/dist/types/src/session/format.d.ts +8 -177
  39. package/dist/types/src/session/format.d.ts.map +1 -1
  40. package/dist/types/src/session/index.d.ts +2 -1
  41. package/dist/types/src/session/index.d.ts.map +1 -1
  42. package/dist/types/src/session/observer.d.ts +7 -6
  43. package/dist/types/src/session/observer.d.ts.map +1 -1
  44. package/dist/types/src/session/request.d.ts +104 -0
  45. package/dist/types/src/session/request.d.ts.map +1 -0
  46. package/dist/types/src/session/toolkit.d.ts +11 -9
  47. package/dist/types/src/session/toolkit.d.ts.map +1 -1
  48. package/dist/types/src/session/version-pin.d.ts +4 -4
  49. package/dist/types/src/session/version-pin.d.ts.map +1 -1
  50. package/dist/types/src/templates/system.d.ts +1 -2
  51. package/dist/types/src/templates/system.d.ts.map +1 -1
  52. package/dist/types/src/tracing.d.ts +233 -0
  53. package/dist/types/src/tracing.d.ts.map +1 -0
  54. package/dist/types/src/types/artifact.d.ts +28 -0
  55. package/dist/types/src/types/artifact.d.ts.map +1 -0
  56. package/dist/types/src/types/index.d.ts +2 -0
  57. package/dist/types/src/types/index.d.ts.map +1 -0
  58. package/dist/types/tsconfig.tsbuildinfo +1 -1
  59. package/package.json +35 -47
  60. package/src/conversation/context.test.ts +52 -0
  61. package/src/conversation/context.ts +324 -75
  62. package/src/conversation/index.ts +1 -1
  63. package/src/conversation/session.ts +334 -0
  64. package/src/diff.test.ts +37 -6
  65. package/src/diff.ts +18 -4
  66. package/src/errors.ts +2 -2
  67. package/src/execution-graph.test.ts +47 -45
  68. package/src/execution-graph.ts +41 -22
  69. package/src/extraction/extraction-ner-function/index.ts +0 -1
  70. package/src/extraction/extraction-ner-function/named-entity-recognition.test.ts +10 -5
  71. package/src/extraction/extraction.ts +44 -15
  72. package/src/extraction/index.ts +0 -1
  73. package/src/extraction/quotes.ts +5 -6
  74. package/src/functions/services.ts +126 -65
  75. package/src/functions.conversations.json +1 -0
  76. package/src/index.ts +1 -0
  77. package/src/session/artifact-diff.ts +2 -2
  78. package/src/session/error-handling.ts +4 -3
  79. package/src/session/format.ts +34 -26
  80. package/src/session/index.ts +2 -1
  81. package/src/session/observer.ts +8 -4
  82. package/src/session/request.conversations.json +1 -0
  83. package/src/session/request.ts +342 -0
  84. package/src/session/toolkit.ts +29 -28
  85. package/src/session/version-pin.ts +7 -7
  86. package/src/templates/{format.tpl → instructions/format.tpl} +1 -4
  87. package/src/templates/{system.tpl → instructions/system.tpl} +50 -2
  88. package/src/templates/system.ts +9 -8
  89. package/src/tracing.ts +57 -0
  90. package/src/{types.ts → types/artifact.ts} +44 -7
  91. package/src/types/index.ts +5 -0
  92. package/dist/lib/browser/chunk-ILL2CN3G.mjs +0 -374
  93. package/dist/lib/browser/chunk-ILL2CN3G.mjs.map +0 -7
  94. package/dist/lib/browser/chunk-UB26N5UF.mjs +0 -89
  95. package/dist/lib/browser/chunk-UB26N5UF.mjs.map +0 -7
  96. package/dist/lib/browser/extraction/index.mjs +0 -272
  97. package/dist/lib/browser/extraction/index.mjs.map +0 -7
  98. package/dist/lib/browser/index.mjs +0 -861
  99. package/dist/lib/browser/index.mjs.map +0 -7
  100. package/dist/lib/browser/meta.json +0 -1
  101. package/dist/lib/browser/named-entity-recognition-QPYHML66.mjs +0 -13
  102. package/dist/lib/browser/named-entity-recognition-QPYHML66.mjs.map +0 -7
  103. package/dist/lib/node-esm/chunk-BCFXTNSK.mjs +0 -91
  104. package/dist/lib/node-esm/chunk-BCFXTNSK.mjs.map +0 -7
  105. package/dist/lib/node-esm/chunk-GVKA7VL3.mjs +0 -376
  106. package/dist/lib/node-esm/chunk-GVKA7VL3.mjs.map +0 -7
  107. package/dist/lib/node-esm/extraction/index.mjs +0 -273
  108. package/dist/lib/node-esm/extraction/index.mjs.map +0 -7
  109. package/dist/lib/node-esm/index.mjs +0 -862
  110. package/dist/lib/node-esm/index.mjs.map +0 -7
  111. package/dist/lib/node-esm/meta.json +0 -1
  112. package/dist/lib/node-esm/named-entity-recognition-2HDF46YX.mjs +0 -14
  113. package/dist/lib/node-esm/named-entity-recognition-2HDF46YX.mjs.map +0 -7
  114. package/dist/types/src/conversation/conversation.d.ts +0 -41
  115. package/dist/types/src/conversation/conversation.d.ts.map +0 -1
  116. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts +0 -4
  117. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.d.ts.map +0 -1
  118. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts +0 -2
  119. package/dist/types/src/extraction/extraction-llm-function/extraction-llm-function.test.d.ts.map +0 -1
  120. package/dist/types/src/extraction/extraction-llm-function/index.d.ts +0 -2
  121. package/dist/types/src/extraction/extraction-llm-function/index.d.ts.map +0 -1
  122. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts +0 -341
  123. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.d.ts.map +0 -1
  124. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts +0 -2
  125. package/dist/types/src/extraction/extraction-ner-function/extraction-ner-function.test.d.ts.map +0 -1
  126. package/dist/types/src/session/format.test.d.ts +0 -2
  127. package/dist/types/src/session/format.test.d.ts.map +0 -1
  128. package/dist/types/src/session/session.d.ts +0 -47
  129. package/dist/types/src/session/session.d.ts.map +0 -1
  130. package/dist/types/src/session/session.test.d.ts +0 -2
  131. package/dist/types/src/session/session.test.d.ts.map +0 -1
  132. package/dist/types/src/summarization/summarization.test.d.ts +0 -2
  133. package/dist/types/src/summarization/summarization.test.d.ts.map +0 -1
  134. package/dist/types/src/types.d.ts +0 -16
  135. package/dist/types/src/types.d.ts.map +0 -1
  136. package/src/conversation/conversation.ts +0 -119
  137. package/src/extraction/extraction-llm-function/extraction-llm-function.test.ts +0 -84
  138. package/src/extraction/extraction-llm-function/extraction-llm-function.ts +0 -72
  139. package/src/extraction/extraction-llm-function/index.ts +0 -5
  140. package/src/extraction/extraction-ner-function/extraction-ner-function.test.ts +0 -115
  141. package/src/extraction/extraction-ner-function/extraction-ner-function.ts +0 -64
  142. package/src/session/format.test.ts +0 -60
  143. package/src/session/session.conversations.json +0 -1
  144. package/src/session/session.test.ts +0 -146
  145. package/src/session/session.ts +0 -206
  146. package/src/summarization/summarization.test.ts +0 -201
  147. /package/src/extraction/extraction-llm-function/{instructions.tpl → transcription-instructions.tpl} +0 -0
@@ -1,72 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- // ISSUE(burdon): defineFunction
6
- // @ts-nocheck
7
-
8
- import { AiService } from '@dxos/ai';
9
- import { Obj } from '@dxos/echo';
10
- import { create } from '@dxos/echo-schema';
11
- import { type FunctionDefinition, defineFunction } from '@dxos/functions';
12
- import { type ContentBlock, DataType } from '@dxos/schema';
13
-
14
- import { AiSession } from '../../session';
15
- import { ExtractionInput, ExtractionOutput } from '../extraction';
16
- import { ReferencedQuotes, insertReferences } from '../quotes';
17
-
18
- import PROMPT from './instructions.tpl?raw';
19
-
20
- export const extractionAnthropicFunction: FunctionDefinition<ExtractionInput, ExtractionOutput> = defineFunction({
21
- key: 'dxos.org/function/extraction/extract-entities',
22
- name: 'Extract Entities',
23
- description: 'Extract entities from the transcript message and add them to the message.',
24
- inputSchema: ExtractionInput,
25
- outputSchema: ExtractionOutput,
26
- handler: async ({ data: { message, objects }, context }) => {
27
- const startTime = performance.now();
28
- const ai = context.getService(AiService.AiService);
29
- const session = new AiSession({ operationModel: 'configured' });
30
- const result = await session.runStructured(ReferencedQuotes, {
31
- generationOptions: {
32
- model: '@anthropic/claude-3-5-haiku-20241022',
33
- },
34
- client: ai.client,
35
- systemPrompt: PROMPT,
36
- history: [
37
- Obj.make(DataType.Message, {
38
- created: new Date().toISOString(),
39
- sender: { role: 'user' },
40
- blocks: [
41
- {
42
- _tag: 'text',
43
- text: `<context>${JSON.stringify(objects)}</context>`,
44
- } satisfies ContentBlock.Text,
45
- {
46
- _tag: 'text',
47
- text: `<transcript>${JSON.stringify(message.blocks)}</transcript>`,
48
- } satisfies ContentBlock.Text,
49
- ],
50
- }),
51
- ],
52
- artifacts: [],
53
- prompt: '',
54
- tools: [],
55
- } as any); // TODO(burdon): Rewrite test.
56
-
57
- return {
58
- timeElapsed: performance.now() - startTime,
59
- message: create(DataType.Message, {
60
- ...message,
61
- blocks: message.blocks.map((block) =>
62
- block._tag !== 'transcript'
63
- ? block
64
- : {
65
- ...block,
66
- text: insertReferences(block.text, result),
67
- },
68
- ),
69
- }),
70
- };
71
- },
72
- });
@@ -1,5 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- export * from './extraction-llm-function';
@@ -1,115 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { beforeAll, describe, test } from 'vitest';
6
-
7
- import { type EchoDatabase } from '@dxos/echo-db';
8
- import { EchoTestBuilder } from '@dxos/echo-db/testing';
9
- import { FunctionExecutor, ServiceContainer } from '@dxos/functions';
10
- import { log } from '@dxos/log';
11
- import { DataType } from '@dxos/schema';
12
- import { Testing, createTestData } from '@dxos/schema/testing';
13
- import { range } from '@dxos/util';
14
-
15
- import { processTranscriptMessage } from '../extraction';
16
-
17
- import { extractionNerFunction } from './extraction-ner-function';
18
-
19
- describe.skip('NER EntityExtraction', () => {
20
- let builder: EchoTestBuilder;
21
- let db: EchoDatabase;
22
- let executor: FunctionExecutor;
23
- let testData: {
24
- transcriptJosiah: DataType.Message[];
25
- transcriptWoflram: DataType.Message[];
26
- transcriptMessages: DataType.Message[];
27
- documents: Testing.DocumentType[];
28
- contacts: Record<string, DataType.Person>;
29
- organizations: Record<string, DataType.Organization>;
30
- };
31
-
32
- const TYPES = [DataType.Organization, DataType.Person, Testing.Contact, Testing.DocumentType];
33
-
34
- beforeAll(async () => {
35
- // TODO(dmaretskyi): Helper to scaffold this from a config.
36
- builder = await new EchoTestBuilder().open();
37
- const { db: db1 } = await builder.createDatabase({ indexing: { vector: true } });
38
- db = db1;
39
- db.graph.schemaRegistry.addSchema(TYPES);
40
- const data = createTestData();
41
- testData = {
42
- transcriptJosiah: data.transcriptJosiah,
43
- transcriptWoflram: data.transcriptWoflram,
44
- transcriptMessages: data.transcriptMessages,
45
- documents: data.documents.map((document) => db.add(document)),
46
- contacts: Object.fromEntries(Object.entries(data.contacts).map(([key, value]) => [key, db.add(value)])),
47
- organizations: Object.fromEntries(Object.entries(data.organizations).map(([key, value]) => [key, db.add(value)])),
48
- };
49
-
50
- await db.flush();
51
- executor = new FunctionExecutor(
52
- new ServiceContainer().setServices({
53
- database: { db },
54
- }),
55
- );
56
- }, 30_000);
57
-
58
- test('should process a transcript block', async () => {
59
- const { transcriptMessages, documents, contacts } = testData;
60
- log.info('context', { documents, contacts });
61
-
62
- for (const message of transcriptMessages) {
63
- log.info('input', message);
64
- const { message: enhancedMessage } = await processTranscriptMessage({
65
- input: {
66
- message,
67
- objects: [...documents, ...Object.values(contacts)],
68
- },
69
- function: extractionNerFunction,
70
- executor,
71
- });
72
- log.info('output', enhancedMessage);
73
- }
74
- });
75
-
76
- test('computational irreducibility', async () => {
77
- const { transcriptWoflram, documents, contacts } = testData;
78
- log.info('context', { documents, contacts });
79
- const message = transcriptWoflram[0];
80
- log.info('input', message);
81
-
82
- await Promise.all(
83
- range(10).map(async () => {
84
- const { message: enhancedMessage, timeElapsed } = await processTranscriptMessage({
85
- input: {
86
- message,
87
- objects: [...documents, ...Object.values(contacts)],
88
- },
89
- function: extractionNerFunction,
90
- executor,
91
- });
92
- log.info('output', { message: enhancedMessage.blocks[0], timeElapsed });
93
- }),
94
- );
95
- });
96
-
97
- test('org and document linking', async () => {
98
- const { transcriptJosiah, documents, contacts, organizations } = testData;
99
- log.info('context', { contacts, organizations, documents });
100
-
101
- for (const message of transcriptJosiah) {
102
- log.info('input', message);
103
-
104
- const { message: enhancedMessage } = await processTranscriptMessage({
105
- input: {
106
- message,
107
- objects: [...documents, ...Object.values(contacts), ...Object.values(organizations)],
108
- },
109
- function: extractionNerFunction,
110
- executor,
111
- });
112
- log.info('output', enhancedMessage);
113
- }
114
- });
115
- });
@@ -1,64 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- // ISSUE(burdon): defineFunction
6
- // @ts-nocheck
7
-
8
- import { asyncTimeout } from '@dxos/async';
9
- import { create } from '@dxos/echo-schema';
10
- import { DatabaseService, defineFunction } from '@dxos/functions';
11
- import { invariant } from '@dxos/invariant';
12
- import { log } from '@dxos/log';
13
- import { DataType } from '@dxos/schema';
14
-
15
- import { ExtractionInput, ExtractionOutput } from '../extraction';
16
- import { findQuotes, insertReferences } from '../quotes';
17
-
18
- /**
19
- * Named Entity Recognition model
20
- */
21
- // TODO(mykola): Make it use vector index to query objects and not pass objects as input.
22
- export const extractionNerFunction = defineFunction({
23
- key: 'dxos.org/function/extraction/extract-entities',
24
- name: 'Extract Entities',
25
- description: 'Extracts named entities from a text',
26
- inputSchema: ExtractionInput,
27
- outputSchema: ExtractionOutput,
28
- handler: async ({ data: { message, options }, context }) => {
29
- log.info('input', { message, options });
30
- const startTime = performance.now();
31
- const { db } = context.getService(DatabaseService);
32
- const { extractFullEntities } = await import('./named-entity-recognition');
33
-
34
- const entitiesPromise = Promise.all(
35
- message.blocks.map(async (block) => {
36
- invariant(block._tag === 'transcript' || block._tag === 'text');
37
- return extractFullEntities(block.text);
38
- }),
39
- ).then((entities) => entities.flat());
40
- const entities = options?.timeout ? await asyncTimeout(entitiesPromise, options.timeout) : await entitiesPromise;
41
-
42
- log.info('entities', { entities });
43
- const quoteReferences = await findQuotes(
44
- entities.map((entity) => entity.word),
45
- db,
46
- );
47
-
48
- const blocksWithReferences = message.blocks.map((block) => {
49
- invariant(block._tag === 'transcript' || block._tag === 'text');
50
- return { ...block, text: insertReferences(block._tag, quoteReferences) };
51
- });
52
-
53
- const messageWithReferences = create(DataType.Message, {
54
- ...message,
55
- blocks: blocksWithReferences,
56
- });
57
-
58
- log.info('output', { messageWithReferences });
59
- return {
60
- message: messageWithReferences,
61
- timeElapsed: performance.now() - startTime,
62
- };
63
- },
64
- });
@@ -1,60 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { beforeAll, describe, it } from '@effect/vitest';
6
- import { Effect } from 'effect';
7
-
8
- 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';
12
- import { DataType } from '@dxos/schema';
13
- import { trim } from '@dxos/util';
14
-
15
- import { createSystemPrompt } from '../templates/system';
16
-
17
- import { formatSystemPrompt } from './format';
18
-
19
- describe('format', () => {
20
- let db: EchoDatabase;
21
- beforeAll(async () => {
22
- const builder = await new EchoTestBuilder().open();
23
- ({ db } = await builder.createDatabase({
24
- types: [DataType.Text, DataType.Organization, Blueprint.Blueprint],
25
- }));
26
- });
27
-
28
- it.effect(
29
- 'should format',
30
- Effect.fn(function* () {
31
- const object = db.add(
32
- Obj.make(DataType.Organization, {
33
- name: 'Test',
34
- website: 'https://www.test.com',
35
- }),
36
- );
37
-
38
- const blueprint = db.add(
39
- Blueprint.make({
40
- key: 'example.com/blueprint/test',
41
- name: 'Test',
42
- instructions: Template.make({
43
- source: trim`
44
- Test
45
- This is the test blueprint.
46
- `,
47
- }),
48
- }),
49
- );
50
-
51
- const output = yield* formatSystemPrompt({
52
- system: createSystemPrompt({}),
53
- blueprints: [blueprint, blueprint],
54
- objects: [object, object],
55
- });
56
-
57
- console.log(output);
58
- }),
59
- );
60
- });
@@ -1 +0,0 @@
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"}}}}]}]}
@@ -1,146 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { Tool, Toolkit } from '@effect/ai';
6
- import { describe, it } from '@effect/vitest';
7
- import { Effect, Layer, Schema } from 'effect';
8
-
9
- import { AiService, MemoizedAiService, ToolExecutionService, ToolResolverService } from '@dxos/ai';
10
- import { AiServiceTestingPreset } from '@dxos/ai/testing';
11
- import { Obj, Type } from '@dxos/echo';
12
- import { TestHelpers } from '@dxos/effect';
13
- import { TracingService } from '@dxos/functions';
14
- import { log } from '@dxos/log';
15
-
16
- import { AiSession } from './session';
17
-
18
- // Define a calendar event artifact schema.
19
- const CalendarEventSchema = Schema.Struct({
20
- title: Schema.String,
21
- startTime: Schema.String,
22
- endTime: Schema.String,
23
- description: Schema.String,
24
- }).pipe(
25
- Type.Obj({
26
- typename: 'example.com/type/CalendarEvent',
27
- version: '0.1.0',
28
- }),
29
- );
30
-
31
- type CalendarEvent = Schema.Schema.Type<typeof CalendarEventSchema>;
32
-
33
- class TestToolkit extends Toolkit.make(
34
- Tool.make('Calculator', {
35
- description: 'Basic calculator tool',
36
- parameters: {
37
- input: Schema.String.annotations({
38
- description: 'The calculation to perform.',
39
- }),
40
- },
41
- success: Schema.Struct({
42
- result: Schema.Number,
43
- }),
44
- failure: Schema.Never,
45
- }),
46
- ) {}
47
-
48
- // Tool handlers.
49
- const toolkitLayer = TestToolkit.toLayer({
50
- Calculator: ({ input }) =>
51
- Effect.gen(function* () {
52
- const result = (() => {
53
- // Restrict to basic arithmetic operations for safety.
54
- const sanitizedInput = input.replace(/[^0-9+\-*/().\s]/g, '');
55
- log.info('calculate', { sanitizedInput });
56
-
57
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
58
- return Function(`"use strict"; return (${sanitizedInput})`)();
59
- })();
60
-
61
- return { result };
62
- }),
63
- });
64
-
65
- const TestLayer = Layer.mergeAll(
66
- toolkitLayer,
67
- AiService.model('@anthropic/claude-3-5-sonnet-20241022'),
68
- TracingService.layerNoop,
69
- ToolResolverService.layerEmpty,
70
- ToolExecutionService.layerEmpty,
71
- ).pipe(
72
- //
73
- Layer.provideMerge(MemoizedAiService.layerTest()),
74
- Layer.provide(AiServiceTestingPreset('direct')),
75
- );
76
-
77
- describe('AiSession', () => {
78
- it.effect(
79
- 'no tools',
80
- Effect.fnUntraced(
81
- function* (_) {
82
- const session = new AiSession({ operationModel: 'configured' });
83
- const response = yield* session.run({
84
- prompt: 'Hello world!',
85
- history: [],
86
- });
87
- log.info('response', { response });
88
- },
89
- Effect.provide(TestLayer),
90
- TestHelpers.provideTestContext,
91
- ),
92
- );
93
-
94
- it.effect(
95
- 'calculator',
96
- Effect.fnUntraced(
97
- function* (_) {
98
- const session = new AiSession({ operationModel: 'configured' });
99
- const toolkit = yield* TestToolkit;
100
- const response = yield* session.run({
101
- toolkit,
102
- prompt: 'What is 10 + 30?',
103
- history: [],
104
- });
105
- log.info('response', { response });
106
- },
107
- Effect.provide(TestLayer),
108
- TestHelpers.provideTestContext,
109
- ),
110
- );
111
- });
112
-
113
- // Travel to rome, florence, livorno, siena, madrid for conferences
114
-
115
- const _CALENDAR_EVENTS: CalendarEvent[] = [
116
- Obj.make(CalendarEventSchema, {
117
- title: 'Exploring Ancient Ruins in Rome',
118
- startTime: '2024-01-01T10:00:00Z',
119
- endTime: '2024-01-01T11:00:00Z',
120
- description: 'Tech conference at the historic Colosseum with networking opportunities',
121
- }),
122
- Obj.make(CalendarEventSchema, {
123
- title: 'Renaissance Tech Summit in Florence',
124
- startTime: '2024-01-01T11:00:00Z',
125
- endTime: '2024-01-01T12:00:00Z',
126
- description: 'Discussing AI innovations surrounded by Renaissance art',
127
- }),
128
- Obj.make(CalendarEventSchema, {
129
- title: 'Travel to Livorno',
130
- startTime: '2024-01-01T12:00:00Z',
131
- endTime: '2024-01-01T13:00:00Z',
132
- description: 'Travel to Livorno',
133
- }),
134
- Obj.make(CalendarEventSchema, {
135
- title: 'Travel to Siena',
136
- startTime: '2024-01-01T13:00:00Z',
137
- endTime: '2024-01-01T14:00:00Z',
138
- description: 'Travel to Siena',
139
- }),
140
- Obj.make(CalendarEventSchema, {
141
- title: 'Travel to Madrid',
142
- startTime: '2024-01-01T14:00:00Z',
143
- endTime: '2024-01-01T15:00:00Z',
144
- description: 'Travel to Madrid',
145
- }),
146
- ];
@@ -1,206 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { type AiError, LanguageModel, type Tool, type Toolkit } from '@effect/ai';
6
- import { Chunk, Effect, type Schema, Stream } from 'effect';
7
-
8
- import {
9
- AiParser,
10
- AiPreprocessor,
11
- type AiToolNotFoundError,
12
- type PromptPreprocessingError,
13
- type ToolExecutionService,
14
- type ToolResolverService,
15
- callTool,
16
- getToolCalls,
17
- } from '@dxos/ai';
18
- import { type Blueprint } from '@dxos/blueprints';
19
- import { todo } from '@dxos/debug';
20
- import { Obj } from '@dxos/echo';
21
- import { TracingService } from '@dxos/functions';
22
- import { log } from '@dxos/log';
23
- import { DataType } from '@dxos/schema';
24
-
25
- import { type AiAssistantError } from '../errors';
26
-
27
- import { formatSystemPrompt, formatUserPrompt } from './format';
28
- import { GenerationObserver } from './observer';
29
-
30
- export type AiSessionRunError = AiError.AiError | PromptPreprocessingError | AiToolNotFoundError | AiAssistantError;
31
-
32
- export type AiSessionRunRequirements =
33
- | LanguageModel.LanguageModel
34
- | ToolExecutionService
35
- | ToolResolverService
36
- | TracingService;
37
-
38
- export type AiSessionRunParams<Tools extends Record<string, Tool.Any>> = {
39
- prompt: string;
40
- system?: string;
41
- history?: DataType.Message[];
42
- objects?: Obj.Any[];
43
- blueprints?: Blueprint.Blueprint[];
44
- toolkit?: Toolkit.WithHandler<Tools>;
45
- observer?: GenerationObserver;
46
- };
47
-
48
- export type AiSessionOptions = {};
49
-
50
- /**
51
- * Contains message history, tools, current context.
52
- * Current context means the state of the app, time of day, and other contextual information.
53
- * It makes requests to the model, its a state machine.
54
- * It keeps track of the current goal.
55
- * It manages the context window.
56
- * Tracks the success criteria of reaching the goal, exposing metrics (stretch).
57
- * Could be run locally in the app or remotely.
58
- * Could be personal or shared.
59
- */
60
- export class AiSession {
61
- /** Prevents concurrent execution of session. */
62
- private readonly _semaphore = Effect.runSync(Effect.makeSemaphore(1));
63
-
64
- /** Message history from queue. */
65
- // TODO(burdon): Evolve into supporting a git-like graph of messages.
66
- private _history: DataType.Message[] = [];
67
-
68
- /** Pending messages for this session (incl. the current prompt). */
69
- private _pending: DataType.Message[] = [];
70
-
71
- constructor(private readonly _options: AiSessionOptions = {}) {}
72
-
73
- run = <Tools extends Record<string, Tool.Any>>({
74
- prompt,
75
- system: systemTemplate,
76
- history = [],
77
- objects = [],
78
- blueprints = [],
79
- toolkit,
80
- observer = GenerationObserver.noop(),
81
- }: AiSessionRunParams<Tools>): Effect.Effect<DataType.Message[], AiSessionRunError, AiSessionRunRequirements> =>
82
- Effect.gen(this, function* () {
83
- this._history = [...history];
84
- this._pending = [];
85
- const pending = this._pending;
86
-
87
- const submitMessage = Effect.fnUntraced(function* (message: DataType.Message) {
88
- pending.push(message);
89
- yield* observer.onMessage(message);
90
- yield* TracingService.emitConverationMessage(message);
91
- return message;
92
- });
93
-
94
- // Submit the prompt.
95
- // TODO(burdon): Remove if cancelled?
96
- const promptMessage = yield* submitMessage(yield* formatUserPrompt({ prompt, history }));
97
-
98
- // Generate system and prompt messages.
99
- const system = yield* formatSystemPrompt({ system: systemTemplate, blueprints, objects });
100
-
101
- // Tool call loop.
102
- do {
103
- log('request', {
104
- prompt: promptMessage,
105
- system: { snippet: createSnippet(system), length: system.length },
106
- pending: this._pending.length,
107
- history: this._history.length,
108
- objects: objects?.length ?? 0,
109
- });
110
-
111
- const prompt = yield* AiPreprocessor.preprocessPrompt([...this._history, ...this._pending], { system });
112
-
113
- // Execute the stream request.
114
- // logDump('prompt', Prompt.Prompt.pipe(Schema.encodeSync)(prompt));
115
- const blocks = yield* LanguageModel.streamText({
116
- prompt,
117
- toolkit,
118
- disableToolCallResolution: true,
119
- }).pipe(
120
- // TOOD(dmaretskyi): Error mapping.
121
- AiParser.parseResponse({
122
- onBegin: () => observer.onBegin(),
123
- onBlock: (block) => observer.onBlock(block),
124
- onPart: (part) => observer.onPart(part),
125
- onEnd: (summary) => observer.onEnd(summary),
126
- }),
127
- Stream.runCollect,
128
- Effect.map(Chunk.toArray),
129
- );
130
-
131
- // Create the response message.
132
- const response = yield* submitMessage(
133
- Obj.make(DataType.Message, {
134
- created: new Date().toISOString(),
135
- sender: { role: 'assistant' },
136
- blocks,
137
- }),
138
- );
139
-
140
- // Parse the response for tool calls.
141
- const toolCalls = getToolCalls(response);
142
- if (toolCalls.length === 0) {
143
- break;
144
- } else if (!toolkit) {
145
- throw new Error('No toolkit provided'); // TODO(burdon): Throw user error?
146
- }
147
-
148
- // Execute the tool calls.
149
- // TODO(burdon): Retry errors? Write result when each completes individually?
150
- const toolResults = yield* Effect.forEach(toolCalls, (toolCall) => {
151
- return callTool(toolkit, toolCall).pipe(
152
- Effect.provide(
153
- TracingService.layerSubframe((context) => ({
154
- ...context,
155
- parentMessage: response.id,
156
- toolCallId: toolCall.toolCallId,
157
- })),
158
- ),
159
- );
160
- });
161
-
162
- // Add to queue and continue loop.
163
- // TODO(wittjosiah): Sometimes tool error results are added to the queue before the tool agent statuses.
164
- // This results in a broken execution graph.
165
- yield* submitMessage(
166
- Obj.make(DataType.Message, {
167
- created: new Date().toISOString(),
168
- sender: { role: 'tool' },
169
- blocks: toolResults,
170
- }),
171
- );
172
- } while (true);
173
-
174
- log('done', { pending: this._pending.length });
175
- return this._pending;
176
- }).pipe(this._semaphore.withPermits(1), Effect.withSpan('AiSession.run'));
177
-
178
- /**
179
- * @deprecated
180
- */
181
- // TODO(burdon): Implement or remove.
182
- async runStructured<S extends Schema.Schema.AnyNoContext>(
183
- _schema: S,
184
- _options: AiSessionRunParams<any>,
185
- ): Promise<Schema.Schema.Type<S>> {
186
- return todo();
187
- // const parser = structuredOutputParser(schema);
188
- // const result = await this.run({
189
- // ...options,
190
- // executableTools: [...(options.executableTools ?? []), parser.tool],
191
- // });
192
- // return parser.getResult(result);
193
- }
194
- }
195
-
196
- const createSnippet = (text: string, len = 32) =>
197
- text.length <= len * 2 ? text : [text.slice(0, len), '...', text.slice(-len)].join('');
198
-
199
- // TODO(dmaretskyi): Extract as a general util.
200
- const logDump = (message: string, data: unknown) => {
201
- // eslint-disable-next-line @typescript-eslint/no-require-imports
202
- const { writeFileSync } = require('node:fs');
203
- const path = `/tmp/log-data-${Date.now()}.json`;
204
- writeFileSync(path, JSON.stringify(data, null, 2));
205
- log.info(message, { path });
206
- };