@dxos/assistant 0.7.4-df37807

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 (128) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +24 -0
  3. package/dist/lib/browser/chunk-AKP45LQH.mjs +1394 -0
  4. package/dist/lib/browser/chunk-AKP45LQH.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +71 -0
  6. package/dist/lib/browser/index.mjs.map +7 -0
  7. package/dist/lib/browser/meta.json +1 -0
  8. package/dist/lib/browser/testing/index.mjs +601 -0
  9. package/dist/lib/browser/testing/index.mjs.map +7 -0
  10. package/dist/lib/node/chunk-G6CGWEMO.cjs +1418 -0
  11. package/dist/lib/node/chunk-G6CGWEMO.cjs.map +7 -0
  12. package/dist/lib/node/index.cjs +93 -0
  13. package/dist/lib/node/index.cjs.map +7 -0
  14. package/dist/lib/node/meta.json +1 -0
  15. package/dist/lib/node/testing/index.cjs +619 -0
  16. package/dist/lib/node/testing/index.cjs.map +7 -0
  17. package/dist/lib/node-esm/chunk-WUPUPEFB.mjs +1396 -0
  18. package/dist/lib/node-esm/chunk-WUPUPEFB.mjs.map +7 -0
  19. package/dist/lib/node-esm/index.mjs +72 -0
  20. package/dist/lib/node-esm/index.mjs.map +7 -0
  21. package/dist/lib/node-esm/meta.json +1 -0
  22. package/dist/lib/node-esm/testing/index.mjs +602 -0
  23. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  24. package/dist/types/src/ai-service/client.d.ts +48 -0
  25. package/dist/types/src/ai-service/client.d.ts.map +1 -0
  26. package/dist/types/src/ai-service/client.test.d.ts +2 -0
  27. package/dist/types/src/ai-service/client.test.d.ts.map +1 -0
  28. package/dist/types/src/ai-service/index.d.ts +3 -0
  29. package/dist/types/src/ai-service/index.d.ts.map +1 -0
  30. package/dist/types/src/ai-service/schema.d.ts +296 -0
  31. package/dist/types/src/ai-service/schema.d.ts.map +1 -0
  32. package/dist/types/src/ai-service/sse.d.ts +34 -0
  33. package/dist/types/src/ai-service/sse.d.ts.map +1 -0
  34. package/dist/types/src/bin/chat.d.ts +2 -0
  35. package/dist/types/src/bin/chat.d.ts.map +1 -0
  36. package/dist/types/src/conversation/conversation.d.ts +23 -0
  37. package/dist/types/src/conversation/conversation.d.ts.map +1 -0
  38. package/dist/types/src/conversation/conversation.test.d.ts +2 -0
  39. package/dist/types/src/conversation/conversation.test.d.ts.map +1 -0
  40. package/dist/types/src/conversation/index.d.ts +3 -0
  41. package/dist/types/src/conversation/index.d.ts.map +1 -0
  42. package/dist/types/src/conversation/types.d.ts +48 -0
  43. package/dist/types/src/conversation/types.d.ts.map +1 -0
  44. package/dist/types/src/cypher/ast.d.ts +56 -0
  45. package/dist/types/src/cypher/ast.d.ts.map +1 -0
  46. package/dist/types/src/cypher/combinators.d.ts +14 -0
  47. package/dist/types/src/cypher/combinators.d.ts.map +1 -0
  48. package/dist/types/src/cypher/combinators.test.d.ts +2 -0
  49. package/dist/types/src/cypher/combinators.test.d.ts.map +1 -0
  50. package/dist/types/src/cypher/echo-data-source.d.ts +16 -0
  51. package/dist/types/src/cypher/echo-data-source.d.ts.map +1 -0
  52. package/dist/types/src/cypher/execution-plan.d.ts +49 -0
  53. package/dist/types/src/cypher/execution-plan.d.ts.map +1 -0
  54. package/dist/types/src/cypher/execution-plan.test.d.ts +2 -0
  55. package/dist/types/src/cypher/execution-plan.test.d.ts.map +1 -0
  56. package/dist/types/src/cypher/index.d.ts +5 -0
  57. package/dist/types/src/cypher/index.d.ts.map +1 -0
  58. package/dist/types/src/cypher/parjs.d.ts +4 -0
  59. package/dist/types/src/cypher/parjs.d.ts.map +1 -0
  60. package/dist/types/src/cypher/parser.d.ts +6 -0
  61. package/dist/types/src/cypher/parser.d.ts.map +1 -0
  62. package/dist/types/src/cypher/query-executor.d.ts +26 -0
  63. package/dist/types/src/cypher/query-executor.d.ts.map +1 -0
  64. package/dist/types/src/cypher/query-executor.test.d.ts +2 -0
  65. package/dist/types/src/cypher/query-executor.test.d.ts.map +1 -0
  66. package/dist/types/src/cypher/schema.d.ts +12 -0
  67. package/dist/types/src/cypher/schema.d.ts.map +1 -0
  68. package/dist/types/src/cypher/schema.test.d.ts +2 -0
  69. package/dist/types/src/cypher/schema.test.d.ts.map +1 -0
  70. package/dist/types/src/index.d.ts +4 -0
  71. package/dist/types/src/index.d.ts.map +1 -0
  72. package/dist/types/src/sanity.test.d.ts +2 -0
  73. package/dist/types/src/sanity.test.d.ts.map +1 -0
  74. package/dist/types/src/testing/index.d.ts +5 -0
  75. package/dist/types/src/testing/index.d.ts.map +1 -0
  76. package/dist/types/src/testing/logger.d.ts +12 -0
  77. package/dist/types/src/testing/logger.d.ts.map +1 -0
  78. package/dist/types/src/testing/object-extraction.test.d.ts +1 -0
  79. package/dist/types/src/testing/object-extraction.test.d.ts.map +1 -0
  80. package/dist/types/src/testing/query-promts.d.ts +5 -0
  81. package/dist/types/src/testing/query-promts.d.ts.map +1 -0
  82. package/dist/types/src/testing/query.test.d.ts +2 -0
  83. package/dist/types/src/testing/query.test.d.ts.map +1 -0
  84. package/dist/types/src/testing/test-data.d.ts +29 -0
  85. package/dist/types/src/testing/test-data.d.ts.map +1 -0
  86. package/dist/types/src/testing/test-schema.d.ts +50 -0
  87. package/dist/types/src/testing/test-schema.d.ts.map +1 -0
  88. package/dist/types/src/util/index.d.ts +2 -0
  89. package/dist/types/src/util/index.d.ts.map +1 -0
  90. package/dist/types/src/util/trim.d.ts +5 -0
  91. package/dist/types/src/util/trim.d.ts.map +1 -0
  92. package/dist/types/tsconfig.tsbuildinfo +1 -0
  93. package/package.json +64 -0
  94. package/src/ai-service/client.test.ts +162 -0
  95. package/src/ai-service/client.ts +243 -0
  96. package/src/ai-service/index.ts +6 -0
  97. package/src/ai-service/schema.ts +260 -0
  98. package/src/ai-service/sse.ts +336 -0
  99. package/src/bin/chat.ts +79 -0
  100. package/src/conversation/conversation.test.ts +74 -0
  101. package/src/conversation/conversation.ts +134 -0
  102. package/src/conversation/index.ts +6 -0
  103. package/src/conversation/types.ts +90 -0
  104. package/src/cypher/ast.ts +75 -0
  105. package/src/cypher/combinators.test.ts +137 -0
  106. package/src/cypher/combinators.ts +149 -0
  107. package/src/cypher/echo-data-source.ts +105 -0
  108. package/src/cypher/execution-plan.test.ts +42 -0
  109. package/src/cypher/execution-plan.ts +208 -0
  110. package/src/cypher/index.ts +8 -0
  111. package/src/cypher/parjs-shims.d.ts +6 -0
  112. package/src/cypher/parjs.ts +10 -0
  113. package/src/cypher/parser.ts +19 -0
  114. package/src/cypher/query-executor.test.ts +47 -0
  115. package/src/cypher/query-executor.ts +145 -0
  116. package/src/cypher/schema.test.ts +17 -0
  117. package/src/cypher/schema.ts +82 -0
  118. package/src/index.ts +7 -0
  119. package/src/sanity.test.ts +9 -0
  120. package/src/testing/index.ts +8 -0
  121. package/src/testing/logger.ts +128 -0
  122. package/src/testing/object-extraction.test.ts +71 -0
  123. package/src/testing/query-promts.ts +75 -0
  124. package/src/testing/query.test.ts +90 -0
  125. package/src/testing/test-data.ts +366 -0
  126. package/src/testing/test-schema.ts +80 -0
  127. package/src/util/index.ts +5 -0
  128. package/src/util/trim.ts +15 -0
@@ -0,0 +1,128 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import type { ImageSource, MessageContentBlock } from '../ai-service';
6
+ import type { ConversationEvent } from '../conversation';
7
+
8
+ export const createLogger = ({
9
+ stream,
10
+ filter,
11
+ onImage,
12
+ }: {
13
+ stream?: boolean;
14
+ filter?: (event: ConversationEvent) => boolean;
15
+ onImage?: (img: { id: string; description?: string; source: ImageSource }) => void;
16
+ } = {}) => {
17
+ const images: Record<string, any> = {};
18
+ let currentText = '';
19
+
20
+ return (event: ConversationEvent) => {
21
+ if (typeof filter === 'function' && !filter(event)) {
22
+ return;
23
+ }
24
+
25
+ const printContentBlock = (content: MessageContentBlock) => {
26
+ switch (content.type) {
27
+ case 'text':
28
+ process.stdout.write(content.text);
29
+ break;
30
+ case 'tool_use':
31
+ process.stdout.write(`⚙️ [Tool Use] ${content.name}\n`);
32
+ break;
33
+ case 'image': {
34
+ if (content.id && content.source) {
35
+ images[content.id] = content.source;
36
+ }
37
+ process.stdout.write(`[Image id=${content.id} media_type=${content.source?.media_type}]`);
38
+ break;
39
+ }
40
+ }
41
+ };
42
+
43
+ const onTextBlockPrinted = (text: string) => {
44
+ const imageMatch = text.match(/<image id="([^"]+)"(?:\s+prompt="([^"]+)")?\s*\/>/);
45
+ if (imageMatch && onImage && images[imageMatch[1]]) {
46
+ onImage({
47
+ id: imageMatch[1],
48
+ description: imageMatch[2], // Extract prompt as description if present
49
+ source: images[imageMatch[1]],
50
+ });
51
+ }
52
+ };
53
+
54
+ if (stream) {
55
+ switch (event.type) {
56
+ case 'message_start': {
57
+ process.stdout.write(`${event.message.role.toUpperCase()}\n\n`);
58
+ for (const content of event.message.content) {
59
+ printContentBlock(content);
60
+ }
61
+ break;
62
+ }
63
+ case 'content_block_start': {
64
+ currentText = '';
65
+ printContentBlock(event.content);
66
+ break;
67
+ }
68
+ case 'content_block_delta': {
69
+ switch (event.delta.type) {
70
+ case 'text_delta': {
71
+ currentText += event.delta.text;
72
+ process.stdout.write(event.delta.text);
73
+ break;
74
+ }
75
+ case 'input_json_delta': {
76
+ process.stdout.write(event.delta.partial_json);
77
+ break;
78
+ }
79
+ }
80
+ break;
81
+ }
82
+ case 'content_block_stop': {
83
+ process.stdout.write('\n');
84
+ onTextBlockPrinted(currentText);
85
+ currentText = '';
86
+ break;
87
+ }
88
+ case 'message_delta': {
89
+ break;
90
+ }
91
+ case 'message_stop': {
92
+ process.stdout.write('\n\n');
93
+ break;
94
+ }
95
+ }
96
+ }
97
+
98
+ switch (event.type) {
99
+ case 'message': {
100
+ if (!stream || event.message.role !== 'assistant') {
101
+ process.stdout.write(`${event.message.role.toUpperCase()}\n\n`);
102
+ for (const block of event.message.content) {
103
+ switch (block.type) {
104
+ case 'text':
105
+ process.stdout.write(block.text + '\n');
106
+ onTextBlockPrinted(block.text);
107
+ break;
108
+ case 'tool_use':
109
+ process.stdout.write(`⚙️ [Tool Use] ${block.name}\n`);
110
+ process.stdout.write(` ${JSON.stringify(block.input)}\n`);
111
+ break;
112
+ case 'tool_result':
113
+ if (block.isError) {
114
+ process.stdout.write('❌ [Tool Error]\n');
115
+ process.stdout.write(block.content + '\n');
116
+ } else {
117
+ process.stdout.write('✅ [Tool Success]\n');
118
+ process.stdout.write(block.content + '\n');
119
+ }
120
+ }
121
+ process.stdout.write('\n\n');
122
+ }
123
+ }
124
+ break;
125
+ }
126
+ }
127
+ };
128
+ };
@@ -0,0 +1,71 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ // import { AnthropicBackend } from '../conversation';
6
+ // import { runLLM } from '../conversation';
7
+ // import { createUserMessage } from '../conversation';
8
+
9
+ // test('extract objects from the DXOS explainer', async () => {
10
+ // const result = await runLLM({
11
+ // model: '@anthropic/claude-3-5-haiku-20241022',
12
+ // messages: [
13
+ // createUserMessage(`
14
+ // # README
15
+
16
+ // > Welcome to DXOS Composer.
17
+
18
+ // Here, you can:
19
+ // - Create and organize things
20
+ // - Share and collaborate with others in real-time
21
+ // - Work offline
22
+
23
+ // Composer is:
24
+ // - Free. Software built with DXOS requires no expensive server infrastructure to operate.
25
+ // - Private. Your data never travels to or lives on any servers. Connections with collaborators are peer-to-peer.
26
+ // - Local-first. Your data is stored entirely in your browser.
27
+ // - Extensible. Add features by building plugins.
28
+ // - Open source.
29
+
30
+ // Your feedback is most welcome: feature requests, bug reports, questions: [Discord](https://discord.gg/uTYyx6srAW).
31
+
32
+ // See also:
33
+ // - [DXOS Documentation](https://docs.dxos.org) where you can build your own local-first, collaborative software.
34
+ // `),
35
+ // ],
36
+ // system: `
37
+ // <instructions>
38
+ // Extract a list of objects that appear in the text.
39
+ // Objects can include names, places, technologies, people, concepts, etc.
40
+ // Extracts objects that are interesting or important in the context of the text.
41
+ // </instructions>
42
+
43
+ // <formatting>
44
+ // Output a list of object names from text, each surrounded with an object <object> tag.
45
+ // </formatting>
46
+
47
+ // <example>
48
+ // <input>
49
+ // Czerniaków was born on 30 November 1880 in Warsaw, Poland (then part of the Congress Poland). He studied engineering in Warsaw and Dresden and taught in the Jewish community's vocational school in Warsaw.
50
+ // </input>
51
+ // <output>
52
+ // <object>Czerniaków</object>
53
+ // <object>30 November 1880</object>
54
+ // <object>Warsaw</object>
55
+ // <object>Poland</object>
56
+ // <object>Congress Poland</object>
57
+ // <object>engineering</object>
58
+ // <object>Dresden</object>
59
+ // </output>
60
+ // </example>
61
+ // `,
62
+ // tools: [],
63
+ // backend,
64
+ // });
65
+
66
+ // log.info('result', { result });
67
+ // });
68
+
69
+ // const backend = new AnthropicBackend({
70
+ // apiKey: process.env.ANTHROPIC_API_KEY!,
71
+ // });
@@ -0,0 +1,75 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { Schema as S } from '@effect/schema';
6
+
7
+ import { toJsonSchema } from '@dxos/echo-schema';
8
+ import { log } from '@dxos/log';
9
+
10
+ import { defineTool, LLMToolResult } from '../conversation';
11
+ import { executeQuery, formatJsonSchemaForLLM, type DataSource } from '../cypher';
12
+ import { trim } from '../util';
13
+
14
+ export const createCypherTool = (dataSource: DataSource, schemaTypes: S.Schema.Any[] = []) =>
15
+ defineTool({
16
+ name: 'graphQuery',
17
+ description:
18
+ 'Query the ECHO graph database in cypher query language. Returns data from the database.' +
19
+ (schemaTypes.length > 0 ? `\n\n${createSystemPrompt(schemaTypes)}` : ''),
20
+ schema: S.Struct({
21
+ query: S.String.annotations({
22
+ description: `
23
+ A valid cypher query string to execute.
24
+ Query must have one MATCH clause.
25
+ Match clause can have multiple patterns but they must all be connected via a relationship chain.
26
+ Query might have zero or one WHERE clauses.
27
+ Query must have one RETURN clause.
28
+ RETURN clause can have multiple expressions separated by commas.
29
+ DISTINCT keyword is not allowed.
30
+
31
+ <example>
32
+ MATCH (n:Person) RETURN n.name
33
+ </example>
34
+
35
+ <example>
36
+ MATCH (s:Studio)-[:PRODUCED]->(m:Movie)-[:PRODUCED_IN]->(c:City {name: "Las Vegas"}) RETURN s
37
+ </example>
38
+
39
+ <example>
40
+ MATCH (a:Actor)-[:STARRED_IN]->(m:Movie)<-[:PRODUCED]-(s:Studio {name: "Cinema Pictures"}) WHERE m.name = "Once upon a time" RETURN m.name, a.name
41
+ </example>
42
+ `,
43
+ }),
44
+ }),
45
+ execute: async ({ query }) => {
46
+ try {
47
+ log('cypher query', { query });
48
+ const results = await executeQuery(dataSource, query);
49
+ log('query complete', { results });
50
+ return LLMToolResult.Success(results);
51
+ } catch (e: any) {
52
+ log.catch(e);
53
+ return LLMToolResult.Error(e.message);
54
+ }
55
+ },
56
+ });
57
+
58
+ export const createSystemPrompt = (schemaTypes: S.Schema.Any[]) => trim`
59
+ You have access to ECHO graph database. You can query the database to get data to satisfy user prompts.
60
+
61
+ Database schema is defined in pseud-cypher syntax. The schema is:
62
+
63
+ <schema>
64
+ ${formatJsonSchemaForLLM(schemaTypes.map((schema) => toJsonSchema(schema)))}
65
+ <schema>
66
+
67
+ Before replying always think step-by-step on how to proceed.
68
+ Print your thoughts inside <cot> tags.
69
+
70
+ <example>
71
+ <cot>To answer the question I need to ...</cot>
72
+ </example>
73
+
74
+ Before answering the user's question, decide what tools you need to use to answer the question.
75
+ `;
@@ -0,0 +1,90 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { test } from 'vitest';
6
+
7
+ import { SpaceId } from '@dxos/keys';
8
+ import { log } from '@dxos/log';
9
+ import { EchoTestBuilder } from '@dxos/echo-db/testing';
10
+
11
+ import { createLogger } from './logger';
12
+ import { createCypherTool, createSystemPrompt } from './query-promts';
13
+ import { createTestData, seedTestData } from './test-data';
14
+ import { Contact, Org, Project, Task } from './test-schema';
15
+ import { AIServiceClientImpl, ObjectId } from '../ai-service';
16
+ import { runLLM, createUserMessage } from '../conversation';
17
+ import { EchoDataSource } from '../cypher/echo-data-source';
18
+
19
+ const ENDPOINT = 'http://localhost:8787';
20
+
21
+ const client = new AIServiceClientImpl({
22
+ endpoint: ENDPOINT,
23
+ });
24
+
25
+ test.skip('cypher query', async () => {
26
+ const dataSource = createTestData();
27
+ const schemaTypes = [Org, Project, Task, Contact];
28
+
29
+ const cypherTool = createCypherTool(dataSource);
30
+
31
+ const spaceId = SpaceId.random();
32
+ const threadId = ObjectId.random();
33
+
34
+ await client.insertMessages([
35
+ createUserMessage(
36
+ spaceId,
37
+ threadId,
38
+ 'Query the database and give me all employees from DXOS organization that work on Composer and what their tasks are.',
39
+ ),
40
+ ]);
41
+
42
+ const result = await runLLM({
43
+ model: '@anthropic/claude-3-5-sonnet-20241022',
44
+ tools: [cypherTool],
45
+ spaceId,
46
+ threadId,
47
+ system: createSystemPrompt(schemaTypes),
48
+ client,
49
+ logger: createLogger({ stream: false }),
50
+ });
51
+
52
+ log.info('DONE', { result: result.result });
53
+ });
54
+
55
+ test.only('query ECHO', async () => {
56
+ await using builder = await new EchoTestBuilder().open();
57
+ const { db, graph } = await builder.createDatabase();
58
+
59
+ const schemaTypes = [Org, Project, Task, Contact];
60
+ graph.schemaRegistry.addSchema(schemaTypes);
61
+
62
+ seedTestData(db);
63
+ await db.flush({ indexes: true });
64
+
65
+ const dataSource = new EchoDataSource(db);
66
+ const cypherTool = createCypherTool(dataSource);
67
+
68
+ const spaceId = SpaceId.random();
69
+ const threadId = ObjectId.random();
70
+
71
+ await client.insertMessages([
72
+ createUserMessage(
73
+ spaceId,
74
+ threadId,
75
+ 'Query the database and give me all employees from DXOS organization that work on Composer and what their tasks are.',
76
+ ),
77
+ ]);
78
+
79
+ const result = await runLLM({
80
+ model: '@anthropic/claude-3-5-sonnet-20241022',
81
+ tools: [cypherTool],
82
+ spaceId,
83
+ threadId,
84
+ system: createSystemPrompt(schemaTypes),
85
+ client,
86
+ logger: createLogger({ stream: false }),
87
+ });
88
+
89
+ log.info('DONE', { result: result.result });
90
+ });