c0de-agent 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/cli/commands/acp.d.ts +19 -0
- package/dist/cli/commands/acp.js +50 -0
- package/dist/cli/commands/chat.d.ts +13 -0
- package/dist/cli/commands/chat.js +26 -0
- package/dist/cli/commands/config.d.ts +17 -0
- package/dist/cli/commands/config.js +77 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.js +14 -0
- package/dist/cli/commands/plugin.d.ts +11 -0
- package/dist/cli/commands/plugin.js +31 -0
- package/dist/cli/commands/serve.d.ts +18 -0
- package/dist/cli/commands/serve.js +35 -0
- package/dist/cli/commands/update.d.ts +24 -0
- package/dist/cli/commands/update.js +40 -0
- package/dist/cli/deps.d.ts +19 -0
- package/dist/cli/deps.js +57 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +149 -0
- package/dist/cli/modes/acp.d.ts +18 -0
- package/dist/cli/modes/acp.js +57 -0
- package/dist/cli/modes/print.d.ts +16 -0
- package/dist/cli/modes/print.js +30 -0
- package/dist/cli/parser.d.ts +19 -0
- package/dist/cli/parser.js +28 -0
- package/dist/cli/utils/output.d.ts +11 -0
- package/dist/cli/utils/output.js +23 -0
- package/dist/cli/utils/prompt.d.ts +12 -0
- package/dist/cli/utils/prompt.js +18 -0
- package/dist/core/agent.d.ts +12 -0
- package/dist/core/agent.js +95 -0
- package/dist/core/agents/builtin.d.ts +6 -0
- package/dist/core/agents/builtin.js +76 -0
- package/dist/core/agents/discovery.d.ts +6 -0
- package/dist/core/agents/discovery.js +96 -0
- package/dist/core/agents/index.d.ts +6 -0
- package/dist/core/agents/index.js +4 -0
- package/dist/core/agents/parallel.d.ts +13 -0
- package/dist/core/agents/parallel.js +37 -0
- package/dist/core/agents/registry.d.ts +4 -0
- package/dist/core/agents/registry.js +23 -0
- package/dist/core/agents/types.d.ts +37 -0
- package/dist/core/agents/types.js +1 -0
- package/dist/core/compact.d.ts +11 -0
- package/dist/core/compact.js +31 -0
- package/dist/core/config.d.ts +7 -0
- package/dist/core/config.js +84 -0
- package/dist/core/context.d.ts +23 -0
- package/dist/core/context.js +93 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +11 -0
- package/dist/core/loop.d.ts +25 -0
- package/dist/core/loop.js +651 -0
- package/dist/core/metrics.d.ts +27 -0
- package/dist/core/metrics.js +94 -0
- package/dist/core/prompt-registry.d.ts +21 -0
- package/dist/core/prompt-registry.js +200 -0
- package/dist/core/prompt.d.ts +11 -0
- package/dist/core/prompt.js +13 -0
- package/dist/core/secret.d.ts +12 -0
- package/dist/core/secret.js +51 -0
- package/dist/core/slash.d.ts +15 -0
- package/dist/core/slash.js +114 -0
- package/dist/core/steering.d.ts +8 -0
- package/dist/core/steering.js +15 -0
- package/dist/core/title.d.ts +42 -0
- package/dist/core/title.js +115 -0
- package/dist/core/tool-exec.d.ts +20 -0
- package/dist/core/tool-exec.js +64 -0
- package/dist/core/types.d.ts +95 -0
- package/dist/core/types.js +1 -0
- package/dist/core/worktree.d.ts +24 -0
- package/dist/core/worktree.js +68 -0
- package/dist/dap/index.d.ts +6 -0
- package/dist/dap/index.js +4 -0
- package/dist/dap/protocol.d.ts +40 -0
- package/dist/dap/protocol.js +132 -0
- package/dist/dap/session.d.ts +23 -0
- package/dist/dap/session.js +130 -0
- package/dist/dap/transport.d.ts +8 -0
- package/dist/dap/transport.js +30 -0
- package/dist/dap/types.d.ts +50 -0
- package/dist/dap/types.js +2 -0
- package/dist/db/client.d.ts +22 -0
- package/dist/db/client.js +39 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.js +4 -0
- package/dist/db/migrate.d.ts +9 -0
- package/dist/db/migrate.js +15 -0
- package/dist/db/schema.d.ts +965 -0
- package/dist/db/schema.js +112 -0
- package/dist/db/types.d.ts +11 -0
- package/dist/db/types.js +1 -0
- package/dist/llm/index.d.ts +13 -0
- package/dist/llm/index.js +8 -0
- package/dist/llm/protocols/openai-compat.d.ts +139 -0
- package/dist/llm/protocols/openai-compat.js +214 -0
- package/dist/llm/protocols/utils/index.d.ts +2 -0
- package/dist/llm/protocols/utils/index.js +2 -0
- package/dist/llm/protocols/utils/lifecycle.d.ts +23 -0
- package/dist/llm/protocols/utils/lifecycle.js +46 -0
- package/dist/llm/protocols/utils/tool-stream.d.ts +46 -0
- package/dist/llm/protocols/utils/tool-stream.js +94 -0
- package/dist/llm/provider-error.d.ts +5 -0
- package/dist/llm/provider-error.js +33 -0
- package/dist/llm/provider.d.ts +32 -0
- package/dist/llm/provider.js +191 -0
- package/dist/llm/registry.d.ts +44 -0
- package/dist/llm/registry.js +107 -0
- package/dist/llm/retry.d.ts +37 -0
- package/dist/llm/retry.js +82 -0
- package/dist/llm/routing.d.ts +36 -0
- package/dist/llm/routing.js +53 -0
- package/dist/llm/schema/errors.d.ts +88 -0
- package/dist/llm/schema/errors.js +33 -0
- package/dist/llm/schema/events.d.ts +135 -0
- package/dist/llm/schema/events.js +35 -0
- package/dist/llm/schema/ids.d.ts +22 -0
- package/dist/llm/schema/ids.js +3 -0
- package/dist/llm/schema/index.d.ts +5 -0
- package/dist/llm/schema/index.js +5 -0
- package/dist/llm/schema/messages.d.ts +97 -0
- package/dist/llm/schema/messages.js +18 -0
- package/dist/llm/schema/options.d.ts +54 -0
- package/dist/llm/schema/options.js +54 -0
- package/dist/llm/token.d.ts +2 -0
- package/dist/llm/token.js +13 -0
- package/dist/llm/transport.d.ts +31 -0
- package/dist/llm/transport.js +165 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +3 -0
- package/dist/plugins/builtin.d.ts +6 -0
- package/dist/plugins/builtin.js +54 -0
- package/dist/plugins/hooks.d.ts +3 -0
- package/dist/plugins/hooks.js +79 -0
- package/dist/plugins/index.d.ts +9 -0
- package/dist/plugins/index.js +8 -0
- package/dist/plugins/init.d.ts +23 -0
- package/dist/plugins/init.js +30 -0
- package/dist/plugins/lifecycle.d.ts +6 -0
- package/dist/plugins/lifecycle.js +74 -0
- package/dist/plugins/loader.d.ts +16 -0
- package/dist/plugins/loader.js +68 -0
- package/dist/plugins/logger.d.ts +4 -0
- package/dist/plugins/logger.js +31 -0
- package/dist/plugins/registry.d.ts +7 -0
- package/dist/plugins/registry.js +24 -0
- package/dist/plugins/types.d.ts +113 -0
- package/dist/plugins/types.js +1 -0
- package/dist/project/detect.d.ts +4 -0
- package/dist/project/detect.js +131 -0
- package/dist/project/index.d.ts +4 -0
- package/dist/project/index.js +2 -0
- package/dist/project/project.d.ts +23 -0
- package/dist/project/project.js +87 -0
- package/dist/project/resolve.d.ts +8 -0
- package/dist/project/resolve.js +81 -0
- package/dist/server/agent-manager.d.ts +32 -0
- package/dist/server/agent-manager.js +54 -0
- package/dist/server/app.d.ts +5 -0
- package/dist/server/app.js +75 -0
- package/dist/server/context.d.ts +20 -0
- package/dist/server/context.js +47 -0
- package/dist/server/dev.d.ts +11 -0
- package/dist/server/dev.js +83 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.js +7 -0
- package/dist/server/middleware/auth.d.ts +15 -0
- package/dist/server/middleware/auth.js +29 -0
- package/dist/server/middleware/cors.d.ts +16 -0
- package/dist/server/middleware/cors.js +44 -0
- package/dist/server/middleware/error.d.ts +15 -0
- package/dist/server/middleware/error.js +11 -0
- package/dist/server/permission/interactive.d.ts +24 -0
- package/dist/server/permission/interactive.js +56 -0
- package/dist/server/permission/store.d.ts +30 -0
- package/dist/server/permission/store.js +47 -0
- package/dist/server/routes/agent.d.ts +4 -0
- package/dist/server/routes/agent.js +19 -0
- package/dist/server/routes/catalog.d.ts +42 -0
- package/dist/server/routes/catalog.js +194 -0
- package/dist/server/routes/chat.d.ts +8 -0
- package/dist/server/routes/chat.js +282 -0
- package/dist/server/routes/commands.d.ts +5 -0
- package/dist/server/routes/commands.js +17 -0
- package/dist/server/routes/config.d.ts +4 -0
- package/dist/server/routes/config.js +27 -0
- package/dist/server/routes/files.d.ts +4 -0
- package/dist/server/routes/files.js +183 -0
- package/dist/server/routes/filesystem.d.ts +29 -0
- package/dist/server/routes/filesystem.js +108 -0
- package/dist/server/routes/health.d.ts +3 -0
- package/dist/server/routes/health.js +13 -0
- package/dist/server/routes/permissions.d.ts +5 -0
- package/dist/server/routes/permissions.js +23 -0
- package/dist/server/routes/project.d.ts +4 -0
- package/dist/server/routes/project.js +51 -0
- package/dist/server/routes/provider.d.ts +16 -0
- package/dist/server/routes/provider.js +66 -0
- package/dist/server/routes/session.d.ts +4 -0
- package/dist/server/routes/session.js +130 -0
- package/dist/server/routes/tool.d.ts +4 -0
- package/dist/server/routes/tool.js +28 -0
- package/dist/server/routes/update.d.ts +14 -0
- package/dist/server/routes/update.js +49 -0
- package/dist/server/server.d.ts +43 -0
- package/dist/server/server.js +197 -0
- package/dist/server/types.d.ts +73 -0
- package/dist/server/types.js +2 -0
- package/dist/server/util/safe-path.d.ts +7 -0
- package/dist/server/util/safe-path.js +7 -0
- package/dist/session/archive.d.ts +15 -0
- package/dist/session/archive.js +129 -0
- package/dist/session/branch.d.ts +9 -0
- package/dist/session/branch.js +63 -0
- package/dist/session/compaction.d.ts +27 -0
- package/dist/session/compaction.js +192 -0
- package/dist/session/context.d.ts +16 -0
- package/dist/session/context.js +173 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.js +10 -0
- package/dist/session/message.d.ts +22 -0
- package/dist/session/message.js +145 -0
- package/dist/session/session.d.ts +37 -0
- package/dist/session/session.js +144 -0
- package/dist/session/snapshot.d.ts +11 -0
- package/dist/session/snapshot.js +60 -0
- package/dist/session/squash.d.ts +9 -0
- package/dist/session/squash.js +88 -0
- package/dist/session/token.d.ts +10 -0
- package/dist/session/token.js +33 -0
- package/dist/session/types.d.ts +122 -0
- package/dist/session/types.js +1 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/types/agent.d.ts +202 -0
- package/dist/shared/types/agent.js +1 -0
- package/dist/shared/types/base.d.ts +32 -0
- package/dist/shared/types/base.js +1 -0
- package/dist/shared/types/config.d.ts +104 -0
- package/dist/shared/types/config.js +1 -0
- package/dist/shared/types/index.d.ts +6 -0
- package/dist/shared/types/index.js +1 -0
- package/dist/shared/types/llm.d.ts +122 -0
- package/dist/shared/types/llm.js +1 -0
- package/dist/shared/types/message.d.ts +71 -0
- package/dist/shared/types/message.js +1 -0
- package/dist/shared/types/tool.d.ts +134 -0
- package/dist/shared/types/tool.js +1 -0
- package/dist/shared/utils/path.d.ts +15 -0
- package/dist/shared/utils/path.js +23 -0
- package/dist/shared/utils.d.ts +5 -0
- package/dist/shared/utils.js +10 -0
- package/dist/tools/builtin/bash.d.ts +12 -0
- package/dist/tools/builtin/bash.js +121 -0
- package/dist/tools/builtin/dap.d.ts +23 -0
- package/dist/tools/builtin/dap.js +231 -0
- package/dist/tools/builtin/edit.d.ts +12 -0
- package/dist/tools/builtin/edit.js +151 -0
- package/dist/tools/builtin/glob.d.ts +11 -0
- package/dist/tools/builtin/glob.js +120 -0
- package/dist/tools/builtin/grep.d.ts +6 -0
- package/dist/tools/builtin/grep.js +141 -0
- package/dist/tools/builtin/read.d.ts +6 -0
- package/dist/tools/builtin/read.js +73 -0
- package/dist/tools/builtin/resolvers.d.ts +12 -0
- package/dist/tools/builtin/resolvers.js +79 -0
- package/dist/tools/builtin/task.d.ts +9 -0
- package/dist/tools/builtin/task.js +100 -0
- package/dist/tools/builtin/write.d.ts +6 -0
- package/dist/tools/builtin/write.js +36 -0
- package/dist/tools/builtin/yield.d.ts +9 -0
- package/dist/tools/builtin/yield.js +40 -0
- package/dist/tools/executor.d.ts +9 -0
- package/dist/tools/executor.js +54 -0
- package/dist/tools/hashline/index.d.ts +2 -0
- package/dist/tools/hashline/index.js +3 -0
- package/dist/tools/hashline/patch.d.ts +48 -0
- package/dist/tools/hashline/patch.js +201 -0
- package/dist/tools/index.d.ts +30 -0
- package/dist/tools/index.js +55 -0
- package/dist/tools/permission.d.ts +13 -0
- package/dist/tools/permission.js +39 -0
- package/dist/tools/registry.d.ts +15 -0
- package/dist/tools/registry.js +54 -0
- package/dist/tools/resolver.d.ts +15 -0
- package/dist/tools/resolver.js +47 -0
- package/dist/tools/truncate.d.ts +8 -0
- package/dist/tools/truncate.js +50 -0
- package/dist/tools/types.d.ts +101 -0
- package/dist/tools/types.js +1 -0
- package/dist/tools/validate.d.ts +8 -0
- package/dist/tools/validate.js +130 -0
- package/dist/tools/websearch/fetch.d.ts +11 -0
- package/dist/tools/websearch/fetch.js +21 -0
- package/dist/tools/websearch/index.d.ts +19 -0
- package/dist/tools/websearch/index.js +78 -0
- package/dist/tools/websearch/providers/brave.d.ts +5 -0
- package/dist/tools/websearch/providers/brave.js +53 -0
- package/dist/tools/websearch/providers/duckduckgo.d.ts +3 -0
- package/dist/tools/websearch/providers/duckduckgo.js +75 -0
- package/dist/tools/websearch/providers/tavily.d.ts +11 -0
- package/dist/tools/websearch/providers/tavily.js +89 -0
- package/dist/tools/websearch/types.d.ts +51 -0
- package/dist/tools/websearch/types.js +18 -0
- package/dist/tools/websearch/websearch.d.ts +10 -0
- package/dist/tools/websearch/websearch.js +48 -0
- package/dist/update/index.d.ts +10 -0
- package/dist/update/index.js +6 -0
- package/dist/update/ipc.d.ts +15 -0
- package/dist/update/ipc.js +41 -0
- package/dist/update/scheduler.d.ts +34 -0
- package/dist/update/scheduler.js +57 -0
- package/dist/update/snapshot.d.ts +39 -0
- package/dist/update/snapshot.js +99 -0
- package/dist/update/updater.d.ts +48 -0
- package/dist/update/updater.js +83 -0
- package/dist/update/version.d.ts +18 -0
- package/dist/update/version.js +49 -0
- package/drizzle/0000_magical_nightshade.sql +55 -0
- package/drizzle/0001_chemical_doctor_faustus.sql +13 -0
- package/drizzle/0002_amusing_micromacro.sql +12 -0
- package/drizzle/0003_regular_purifiers.sql +2 -0
- package/drizzle/meta/0000_snapshot.json +448 -0
- package/drizzle/meta/0001_snapshot.json +539 -0
- package/drizzle/meta/0002_snapshot.json +633 -0
- package/drizzle/meta/0003_snapshot.json +645 -0
- package/drizzle/meta/_journal.json +34 -0
- package/package.json +100 -8
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
|
2
|
+
import { index, integer, jsonb, pgTable, real, text, timestamp, uniqueIndex, uuid, } from 'drizzle-orm/pg-core';
|
|
3
|
+
/**
|
|
4
|
+
* Projects table — git repos or plain directories tracked for agent workspaces.
|
|
5
|
+
* Identity: sha256(worktree)[:16](git 仓库用仓库根,普通目录用绝对路径)。gitRemote 仅作
|
|
6
|
+
* 元数据,不参与 id——避免「先无 remote 注册、后加 remote」导致 id 漂移。
|
|
7
|
+
*/
|
|
8
|
+
export const projects = pgTable('projects', {
|
|
9
|
+
id: text('id').primaryKey(),
|
|
10
|
+
worktree: text('worktree').notNull(),
|
|
11
|
+
vcs: text('vcs'),
|
|
12
|
+
name: text('name'),
|
|
13
|
+
gitRemote: text('git_remote'),
|
|
14
|
+
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
15
|
+
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Sessions table — conversation sessions with branching support.
|
|
19
|
+
* Root sessions have parentId = null. Forked sessions reference their parent.
|
|
20
|
+
*/
|
|
21
|
+
export const sessions = pgTable('sessions', {
|
|
22
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
23
|
+
title: text('title').notNull(),
|
|
24
|
+
parentId: uuid('parent_id').references(() => sessions.id),
|
|
25
|
+
projectId: text('project_id').references(() => projects.id, {
|
|
26
|
+
onDelete: 'set null',
|
|
27
|
+
}),
|
|
28
|
+
branchPoint: integer('branch_point'),
|
|
29
|
+
metadata: jsonb('metadata').notNull().default({}),
|
|
30
|
+
agentType: text('agent_type'),
|
|
31
|
+
worktreePath: text('worktree_path'),
|
|
32
|
+
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
33
|
+
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
|
34
|
+
}, (table) => [
|
|
35
|
+
index('idx_sessions_parent').on(table.parentId),
|
|
36
|
+
index('idx_sessions_project').on(table.projectId),
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Session entries table — unified storage for all session content types.
|
|
40
|
+
* The `tag` field discriminates: 'message' | 'tool_call' | 'tool_result' |
|
|
41
|
+
* 'compaction' | 'squash' | 'branch_summary' | 'steering' | 'file_snapshot'.
|
|
42
|
+
*/
|
|
43
|
+
export const sessionEntries = pgTable('session_entries', {
|
|
44
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
45
|
+
sessionId: uuid('session_id')
|
|
46
|
+
.notNull()
|
|
47
|
+
.references(() => sessions.id, { onDelete: 'cascade' }),
|
|
48
|
+
tag: text('tag').notNull(),
|
|
49
|
+
role: text('role'),
|
|
50
|
+
content: jsonb('content').notNull(),
|
|
51
|
+
toolName: text('tool_name'),
|
|
52
|
+
tokenCount: integer('token_count').default(0),
|
|
53
|
+
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
54
|
+
}, (table) => [index('idx_entries_session').on(table.sessionId, table.createdAt)]);
|
|
55
|
+
/**
|
|
56
|
+
* Compaction archives — stores original entries that were compacted/squashed.
|
|
57
|
+
* Searchable via full-text search on `searchableText`.
|
|
58
|
+
*/
|
|
59
|
+
export const compactionArchives = pgTable('compaction_archives', {
|
|
60
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
61
|
+
sessionId: uuid('session_id')
|
|
62
|
+
.notNull()
|
|
63
|
+
.references(() => sessions.id, { onDelete: 'cascade' }),
|
|
64
|
+
compactionId: uuid('compaction_id').notNull(),
|
|
65
|
+
archiveType: text('archive_type').notNull(),
|
|
66
|
+
originalEntries: jsonb('original_entries').notNull(),
|
|
67
|
+
fileSnapshots: jsonb('file_snapshots').default([]),
|
|
68
|
+
summary: text('summary').notNull(),
|
|
69
|
+
tokenCount: integer('token_count'),
|
|
70
|
+
searchableText: text('searchable_text'),
|
|
71
|
+
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
72
|
+
}, (table) => [
|
|
73
|
+
index('idx_archives_session').on(table.sessionId),
|
|
74
|
+
// Full-text search index (GIN) for archive content search
|
|
75
|
+
sql `CREATE INDEX IF NOT EXISTS "idx_archives_search" ON "compaction_archives" USING gin(to_tsvector('english', coalesce(${table.searchableText}, '')))`,
|
|
76
|
+
]);
|
|
77
|
+
/**
|
|
78
|
+
* File snapshots — caches hot file content to avoid repeated read tool calls.
|
|
79
|
+
* Versioned per (session_id, file_path).
|
|
80
|
+
*/
|
|
81
|
+
export const fileSnapshots = pgTable('file_snapshots', {
|
|
82
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
83
|
+
sessionId: uuid('session_id')
|
|
84
|
+
.notNull()
|
|
85
|
+
.references(() => sessions.id, { onDelete: 'cascade' }),
|
|
86
|
+
entryId: uuid('entry_id'),
|
|
87
|
+
filePath: text('file_path').notNull(),
|
|
88
|
+
content: text('content').notNull(),
|
|
89
|
+
contentHash: text('content_hash').notNull(),
|
|
90
|
+
tokenCount: integer('token_count').default(0),
|
|
91
|
+
version: integer('version').default(1),
|
|
92
|
+
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
93
|
+
}, (table) => [
|
|
94
|
+
index('idx_snapshots_session_path').on(table.sessionId, table.filePath),
|
|
95
|
+
uniqueIndex('idx_snapshots_latest').on(table.sessionId, table.filePath, table.version),
|
|
96
|
+
]);
|
|
97
|
+
/**
|
|
98
|
+
* Tool metrics table — per-(model, tool, mode) success/latency stats used to
|
|
99
|
+
* auto-select the best tool mode (spec §16.5). One row per unique combination.
|
|
100
|
+
*/
|
|
101
|
+
export const toolMetrics = pgTable('tool_metrics', {
|
|
102
|
+
model: text('model').notNull(),
|
|
103
|
+
tool: text('tool').notNull(),
|
|
104
|
+
mode: text('mode').notNull(),
|
|
105
|
+
attempts: integer('attempts').notNull().default(0),
|
|
106
|
+
successes: integer('successes').notNull().default(0),
|
|
107
|
+
failures: integer('failures').notNull().default(0),
|
|
108
|
+
avgLatencyMs: real('avg_latency_ms').notNull().default(0),
|
|
109
|
+
lastUsed: timestamp('last_used', { withTimezone: true }).notNull().defaultNow(),
|
|
110
|
+
}, (table) => [
|
|
111
|
+
uniqueIndex('uq_tool_metrics_model_tool_mode').on(table.model, table.tool, table.mode),
|
|
112
|
+
]);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Database driver selection. */
|
|
2
|
+
type DBDriver = 'pglite' | 'postgres';
|
|
3
|
+
/** Configuration for creating a database connection. */
|
|
4
|
+
type DBConfig = {
|
|
5
|
+
driver: 'pglite';
|
|
6
|
+
dataDir?: string;
|
|
7
|
+
} | {
|
|
8
|
+
driver: 'postgres';
|
|
9
|
+
connectionString: string;
|
|
10
|
+
};
|
|
11
|
+
export type { DBConfig, DBDriver };
|
package/dist/db/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type { RouteConfig } from './protocols/openai-compat.js';
|
|
2
|
+
export { openAICompatRoute } from './protocols/openai-compat.js';
|
|
3
|
+
export type { ChatOptions, ProviderContext } from './provider.js';
|
|
4
|
+
export { buildInternalRequest, chat, chatStream, toInternalMessage, toStreamChunk, } from './provider.js';
|
|
5
|
+
export { isContextOverflow, isContextOverflowFailure } from './provider-error.js';
|
|
6
|
+
export type { ProviderInput, Registry, ResolveResult } from './registry.js';
|
|
7
|
+
export { builtinCapabilities, createRegistry, registerProvider, resolveModelByRole, resolveRoute, setRole, } from './registry.js';
|
|
8
|
+
export type { Retryable, RetryOptions } from './retry.js';
|
|
9
|
+
export { delay, retryable, withRetry } from './retry.js';
|
|
10
|
+
export type { FallbackChain } from './routing.js';
|
|
11
|
+
export { runWithFallback, shouldFallOver } from './routing.js';
|
|
12
|
+
export * from './schema/index.js';
|
|
13
|
+
export { estimateTokens } from './token.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { openAICompatRoute } from './protocols/openai-compat.js';
|
|
2
|
+
export { buildInternalRequest, chat, chatStream, toInternalMessage, toStreamChunk, } from './provider.js';
|
|
3
|
+
export { isContextOverflow, isContextOverflowFailure } from './provider-error.js';
|
|
4
|
+
export { builtinCapabilities, createRegistry, registerProvider, resolveModelByRole, resolveRoute, setRole, } from './registry.js';
|
|
5
|
+
export { delay, retryable, withRetry } from './retry.js';
|
|
6
|
+
export { runWithFallback, shouldFallOver } from './routing.js';
|
|
7
|
+
export * from './schema/index.js';
|
|
8
|
+
export { estimateTokens } from './token.js';
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { StreamEvent } from '../schema/events.js';
|
|
2
|
+
import type { FinishReason } from '../schema/ids.js';
|
|
3
|
+
import type { InternalRequest } from '../schema/messages.js';
|
|
4
|
+
import type { RouteDefaults } from '../schema/options.js';
|
|
5
|
+
import { type LifecycleState } from './utils/lifecycle.js';
|
|
6
|
+
import { type ToolStreamState } from './utils/tool-stream.js';
|
|
7
|
+
type OpenAIChatRole = 'system' | 'user' | 'assistant' | 'tool';
|
|
8
|
+
type OpenAIContentPart = {
|
|
9
|
+
type: 'text';
|
|
10
|
+
text: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'image_url';
|
|
13
|
+
image_url: {
|
|
14
|
+
url: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
type OpenAIMessage = {
|
|
18
|
+
role: OpenAIChatRole;
|
|
19
|
+
content: string | OpenAIContentPart[];
|
|
20
|
+
tool_call_id?: string;
|
|
21
|
+
tool_calls?: {
|
|
22
|
+
id: string;
|
|
23
|
+
type: 'function';
|
|
24
|
+
function: {
|
|
25
|
+
name: string;
|
|
26
|
+
arguments: string;
|
|
27
|
+
};
|
|
28
|
+
}[];
|
|
29
|
+
};
|
|
30
|
+
type OpenAITool = {
|
|
31
|
+
type: 'function';
|
|
32
|
+
function: {
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
parameters: Record<string, unknown>;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
type OpenAIBody = {
|
|
39
|
+
model: string;
|
|
40
|
+
messages: OpenAIMessage[];
|
|
41
|
+
tools?: OpenAITool[];
|
|
42
|
+
tool_choice?: 'auto' | 'none' | 'required' | {
|
|
43
|
+
type: 'function';
|
|
44
|
+
function: {
|
|
45
|
+
name: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
stream: true;
|
|
49
|
+
max_tokens?: number;
|
|
50
|
+
temperature?: number;
|
|
51
|
+
top_p?: number;
|
|
52
|
+
stream_options?: {
|
|
53
|
+
include_usage: boolean;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
/** Build an OpenAI Chat Completions request body from an InternalRequest. */
|
|
57
|
+
declare const bodyFrom: (request: InternalRequest) => OpenAIBody;
|
|
58
|
+
type OpenAIDelta = {
|
|
59
|
+
content?: string;
|
|
60
|
+
reasoning_content?: string;
|
|
61
|
+
reasoning?: string;
|
|
62
|
+
tool_calls?: {
|
|
63
|
+
index: number;
|
|
64
|
+
id?: string;
|
|
65
|
+
function?: {
|
|
66
|
+
name?: string;
|
|
67
|
+
arguments?: string;
|
|
68
|
+
};
|
|
69
|
+
}[];
|
|
70
|
+
};
|
|
71
|
+
type OpenAIChoice = {
|
|
72
|
+
delta?: OpenAIDelta;
|
|
73
|
+
finish_reason?: string | null;
|
|
74
|
+
};
|
|
75
|
+
type OpenAIStreamChunk = {
|
|
76
|
+
choices?: OpenAIChoice[];
|
|
77
|
+
usage?: {
|
|
78
|
+
prompt_tokens?: number;
|
|
79
|
+
completion_tokens?: number;
|
|
80
|
+
total_tokens?: number;
|
|
81
|
+
prompt_tokens_details?: {
|
|
82
|
+
cached_tokens?: number;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
declare const mapFinishReason: (raw: string | null | undefined) => FinishReason;
|
|
87
|
+
type StepState = {
|
|
88
|
+
lifecycle: LifecycleState;
|
|
89
|
+
tools: ToolStreamState;
|
|
90
|
+
/** Finish reason seen but not yet finalized (waiting for trailing usage chunk). */
|
|
91
|
+
pendingFinish: FinishReason | null;
|
|
92
|
+
};
|
|
93
|
+
declare const initialStepState: () => StepState;
|
|
94
|
+
/**
|
|
95
|
+
* Fold one OpenAI stream chunk into (next state, events).
|
|
96
|
+
*
|
|
97
|
+
* OpenAI sends finish_reason in the penultimate chunk and usage in a SEPARATE
|
|
98
|
+
* trailing chunk (choices: []) when stream_options.include_usage is set. So we
|
|
99
|
+
* defer finalization: on finish_reason without usage we stash the reason; the
|
|
100
|
+
* trailing usage chunk triggers the finish. `done` is only true once finalized.
|
|
101
|
+
*/
|
|
102
|
+
declare const step: (state: StepState, chunk: OpenAIStreamChunk) => {
|
|
103
|
+
state: StepState;
|
|
104
|
+
events: StreamEvent[];
|
|
105
|
+
done: boolean;
|
|
106
|
+
};
|
|
107
|
+
/** Finalize a stream that ended without a trailing usage chunk (no-op if already finished). */
|
|
108
|
+
declare const finalizeStream: (state: StepState) => {
|
|
109
|
+
events: StreamEvent[];
|
|
110
|
+
};
|
|
111
|
+
/** Parse a raw SSE data string into an OpenAI stream chunk; throws on bad JSON. */
|
|
112
|
+
declare const parseChunk: (routeId: string, raw: string) => OpenAIStreamChunk;
|
|
113
|
+
type RouteConfig = {
|
|
114
|
+
id: string;
|
|
115
|
+
provider: string;
|
|
116
|
+
baseURL: string;
|
|
117
|
+
apiKey: string;
|
|
118
|
+
/** Optional extra headers (e.g. anthropic-version on compat proxies). */
|
|
119
|
+
headers?: () => Record<string, string>;
|
|
120
|
+
defaults?: RouteDefaults;
|
|
121
|
+
/** Path override; defaults to /v1/chat/completions. */
|
|
122
|
+
path?: string;
|
|
123
|
+
};
|
|
124
|
+
/** Build a complete route descriptor for an OpenAI-compatible provider. */
|
|
125
|
+
declare const openAICompatRoute: (config: RouteConfig) => {
|
|
126
|
+
id: string;
|
|
127
|
+
provider: string;
|
|
128
|
+
protocol: "openai-compat";
|
|
129
|
+
baseURL: string;
|
|
130
|
+
path: string;
|
|
131
|
+
headers: () => Record<string, string>;
|
|
132
|
+
auth: {
|
|
133
|
+
type: "bearer";
|
|
134
|
+
apiKey: string;
|
|
135
|
+
};
|
|
136
|
+
defaults?: RouteDefaults;
|
|
137
|
+
};
|
|
138
|
+
export type { OpenAIBody, OpenAIChatRole, OpenAIChoice, OpenAIContentPart, OpenAIMessage, OpenAIStreamChunk, OpenAITool, RouteConfig, StepState, };
|
|
139
|
+
export { bodyFrom, finalizeStream, initialStepState, mapFinishReason, openAICompatRoute, parseChunk, step, };
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { llmError } from '../schema/errors.js';
|
|
2
|
+
import { finish as lifecycleFinish, initial as lifecycleInitial, reasoningDelta, textDelta, } from './utils/lifecycle.js';
|
|
3
|
+
import { appendOrStart, empty as emptyTools, finishAll, } from './utils/tool-stream.js';
|
|
4
|
+
/** Merge system parts into a single system string (OpenAI Chat has one system slot). */
|
|
5
|
+
const joinSystem = (request) => request.system.map((p) => p.text).join('\n\n');
|
|
6
|
+
const toOpenAIContent = (part) => {
|
|
7
|
+
if (part.type === 'text')
|
|
8
|
+
return { type: 'text', text: part.text };
|
|
9
|
+
return { type: 'text', text: '' };
|
|
10
|
+
};
|
|
11
|
+
const messageToOpenAI = (msg) => {
|
|
12
|
+
if (msg.role === 'system') {
|
|
13
|
+
return {
|
|
14
|
+
role: 'system',
|
|
15
|
+
content: msg.content.map((p) => (p.type === 'text' ? p.text : '')).join(''),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (msg.role === 'tool') {
|
|
19
|
+
const toolPart = msg.content.find((p) => p.type === 'tool-result');
|
|
20
|
+
if (toolPart && toolPart.type === 'tool-result') {
|
|
21
|
+
const text = toolPart.result.type === 'text'
|
|
22
|
+
? String(toolPart.result.value)
|
|
23
|
+
: JSON.stringify(toolPart.result.value);
|
|
24
|
+
return { role: 'tool', content: text, tool_call_id: toolPart.id };
|
|
25
|
+
}
|
|
26
|
+
return { role: 'tool', content: '' };
|
|
27
|
+
}
|
|
28
|
+
const textParts = msg.content.filter((p) => p.type === 'text');
|
|
29
|
+
const toolCalls = msg.content.filter((p) => p.type === 'tool-call');
|
|
30
|
+
const content = msg.role === 'user'
|
|
31
|
+
? textParts.map(toOpenAIContent)
|
|
32
|
+
: textParts.map((p) => (p.type === 'text' ? p.text : '')).join('');
|
|
33
|
+
const message = {
|
|
34
|
+
role: msg.role,
|
|
35
|
+
content,
|
|
36
|
+
};
|
|
37
|
+
if (toolCalls.length > 0) {
|
|
38
|
+
message.tool_calls = toolCalls.map((tc) => ({
|
|
39
|
+
id: tc.id,
|
|
40
|
+
type: 'function',
|
|
41
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.input ?? {}) },
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
return message;
|
|
45
|
+
};
|
|
46
|
+
const toolToOpenAI = (tool) => ({
|
|
47
|
+
type: 'function',
|
|
48
|
+
function: {
|
|
49
|
+
name: tool.name,
|
|
50
|
+
description: tool.description,
|
|
51
|
+
parameters: tool.inputSchema,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
/** Build an OpenAI Chat Completions request body from an InternalRequest. */
|
|
55
|
+
const bodyFrom = (request) => {
|
|
56
|
+
const systemText = joinSystem(request);
|
|
57
|
+
const messages = [];
|
|
58
|
+
if (systemText.length > 0) {
|
|
59
|
+
messages.push({ role: 'system', content: systemText });
|
|
60
|
+
}
|
|
61
|
+
for (const msg of request.messages) {
|
|
62
|
+
messages.push(messageToOpenAI(msg));
|
|
63
|
+
}
|
|
64
|
+
const generation = request.generation;
|
|
65
|
+
const body = {
|
|
66
|
+
model: request.model.id,
|
|
67
|
+
messages,
|
|
68
|
+
stream: true,
|
|
69
|
+
stream_options: { include_usage: true },
|
|
70
|
+
};
|
|
71
|
+
if (request.tools.length > 0) {
|
|
72
|
+
body.tools = request.tools.map(toolToOpenAI);
|
|
73
|
+
if (request.toolChoice !== undefined) {
|
|
74
|
+
body.tool_choice =
|
|
75
|
+
request.toolChoice.type === 'tool'
|
|
76
|
+
? { type: 'function', function: { name: request.toolChoice.name } }
|
|
77
|
+
: request.toolChoice.type;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (generation?.maxTokens !== undefined)
|
|
81
|
+
body.max_tokens = generation.maxTokens;
|
|
82
|
+
if (generation?.temperature !== undefined)
|
|
83
|
+
body.temperature = generation.temperature;
|
|
84
|
+
if (generation?.topP !== undefined)
|
|
85
|
+
body.top_p = generation.topP;
|
|
86
|
+
return body;
|
|
87
|
+
};
|
|
88
|
+
const mapFinishReason = (raw) => {
|
|
89
|
+
switch (raw) {
|
|
90
|
+
case 'stop':
|
|
91
|
+
return 'stop';
|
|
92
|
+
case 'length':
|
|
93
|
+
return 'length';
|
|
94
|
+
case 'tool_calls':
|
|
95
|
+
case 'function_call':
|
|
96
|
+
return 'tool-calls';
|
|
97
|
+
case 'content_filter':
|
|
98
|
+
return 'content-filter';
|
|
99
|
+
default:
|
|
100
|
+
return 'unknown';
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const initialStepState = () => ({
|
|
104
|
+
lifecycle: lifecycleInitial(),
|
|
105
|
+
tools: emptyTools(),
|
|
106
|
+
pendingFinish: null,
|
|
107
|
+
});
|
|
108
|
+
/** Map an OpenAI usage object to the internal Usage fields. */
|
|
109
|
+
const mapUsage = (raw) => ({
|
|
110
|
+
inputTokens: raw.prompt_tokens,
|
|
111
|
+
outputTokens: raw.completion_tokens,
|
|
112
|
+
cacheReadInputTokens: raw.prompt_tokens_details?.cached_tokens,
|
|
113
|
+
totalTokens: raw.total_tokens,
|
|
114
|
+
providerMetadata: { openai: raw },
|
|
115
|
+
});
|
|
116
|
+
/** Finalize the stream: close open blocks, emit step-finish + finish. */
|
|
117
|
+
const finalize = (lifecycle, events, reason, usage) => lifecycleFinish(lifecycle, events, { reason, usage }).state;
|
|
118
|
+
/**
|
|
119
|
+
* Fold one OpenAI stream chunk into (next state, events).
|
|
120
|
+
*
|
|
121
|
+
* OpenAI sends finish_reason in the penultimate chunk and usage in a SEPARATE
|
|
122
|
+
* trailing chunk (choices: []) when stream_options.include_usage is set. So we
|
|
123
|
+
* defer finalization: on finish_reason without usage we stash the reason; the
|
|
124
|
+
* trailing usage chunk triggers the finish. `done` is only true once finalized.
|
|
125
|
+
*/
|
|
126
|
+
const step = (state, chunk) => {
|
|
127
|
+
const events = [];
|
|
128
|
+
let lifecycle = state.lifecycle;
|
|
129
|
+
let tools = state.tools;
|
|
130
|
+
let pendingFinish = state.pendingFinish;
|
|
131
|
+
let done = false;
|
|
132
|
+
const choice = chunk.choices?.[0];
|
|
133
|
+
if (choice !== undefined && choice.delta !== undefined) {
|
|
134
|
+
const delta = choice.delta;
|
|
135
|
+
if (delta.content !== undefined && delta.content.length > 0) {
|
|
136
|
+
lifecycle = textDelta(lifecycle, events, 'text-main', delta.content).state;
|
|
137
|
+
}
|
|
138
|
+
const reasoning = delta.reasoning_content ?? delta.reasoning;
|
|
139
|
+
if (reasoning !== undefined && reasoning.length > 0) {
|
|
140
|
+
lifecycle = reasoningDelta(lifecycle, events, 'reasoning-main', reasoning).state;
|
|
141
|
+
}
|
|
142
|
+
if (delta.tool_calls !== undefined) {
|
|
143
|
+
for (const tc of delta.tool_calls) {
|
|
144
|
+
const outcome = appendOrStart(tools, {
|
|
145
|
+
index: tc.index,
|
|
146
|
+
id: tc.id,
|
|
147
|
+
name: tc.function?.name,
|
|
148
|
+
argumentsDelta: tc.function?.arguments,
|
|
149
|
+
});
|
|
150
|
+
tools = outcome.state;
|
|
151
|
+
events.push(...outcome.events);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// 排除空字符串:部分 provider(如 sensenova)在进行中的 chunk 发送
|
|
155
|
+
// finish_reason:"" 而非标准 null。若不排除,每个 chunk 都会触发 finishAll,
|
|
156
|
+
// 在 tool arguments 尚未累积完毕时就把 input 解析为 {},并清空 tools 状态,
|
|
157
|
+
// 导致后续 arguments delta 全部丢失(tool 参数永远为空,工具调用全部失败)。
|
|
158
|
+
if (choice.finish_reason !== undefined &&
|
|
159
|
+
choice.finish_reason !== null &&
|
|
160
|
+
choice.finish_reason !== '') {
|
|
161
|
+
const finishTools = finishAll(tools);
|
|
162
|
+
tools = finishTools.state;
|
|
163
|
+
events.push(...finishTools.events);
|
|
164
|
+
pendingFinish = mapFinishReason(choice.finish_reason);
|
|
165
|
+
if (chunk.usage !== undefined) {
|
|
166
|
+
lifecycle = finalize(lifecycle, events, pendingFinish, mapUsage(chunk.usage));
|
|
167
|
+
done = true;
|
|
168
|
+
pendingFinish = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else if (chunk.usage !== undefined) {
|
|
173
|
+
// Trailing usage-only chunk (choices empty) — finalize with stashed reason.
|
|
174
|
+
const reason = pendingFinish ?? 'stop';
|
|
175
|
+
lifecycle = finalize(lifecycle, events, reason, mapUsage(chunk.usage));
|
|
176
|
+
done = true;
|
|
177
|
+
pendingFinish = null;
|
|
178
|
+
}
|
|
179
|
+
return { state: { lifecycle, tools, pendingFinish }, events, done };
|
|
180
|
+
};
|
|
181
|
+
/** Finalize a stream that ended without a trailing usage chunk (no-op if already finished). */
|
|
182
|
+
const finalizeStream = (state) => {
|
|
183
|
+
if (state.pendingFinish === null)
|
|
184
|
+
return { events: [] };
|
|
185
|
+
const events = [];
|
|
186
|
+
const lifecycle = finalize(state.lifecycle, events, state.pendingFinish);
|
|
187
|
+
void lifecycle;
|
|
188
|
+
return { events };
|
|
189
|
+
};
|
|
190
|
+
/** Parse a raw SSE data string into an OpenAI stream chunk; throws on bad JSON. */
|
|
191
|
+
const parseChunk = (routeId, raw) => {
|
|
192
|
+
try {
|
|
193
|
+
return JSON.parse(raw);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
throw llmError(routeId, 'stream', {
|
|
197
|
+
_tag: 'InvalidProviderOutput',
|
|
198
|
+
message: 'Invalid OpenAI stream event',
|
|
199
|
+
raw,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
/** Build a complete route descriptor for an OpenAI-compatible provider. */
|
|
204
|
+
const openAICompatRoute = (config) => ({
|
|
205
|
+
id: config.id,
|
|
206
|
+
provider: config.provider,
|
|
207
|
+
protocol: 'openai-compat',
|
|
208
|
+
baseURL: config.baseURL,
|
|
209
|
+
path: config.path ?? '/v1/chat/completions',
|
|
210
|
+
headers: config.headers ?? (() => ({})),
|
|
211
|
+
auth: { type: 'bearer', apiKey: config.apiKey },
|
|
212
|
+
defaults: config.defaults,
|
|
213
|
+
});
|
|
214
|
+
export { bodyFrom, finalizeStream, initialStepState, mapFinishReason, openAICompatRoute, parseChunk, step, };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { StreamEvent, Usage } from '../../schema/events.js';
|
|
2
|
+
import type { ContentBlockID, FinishReason } from '../../schema/ids.js';
|
|
3
|
+
type LifecycleState = {
|
|
4
|
+
stepStarted: boolean;
|
|
5
|
+
text: Set<ContentBlockID>;
|
|
6
|
+
reasoning: Set<ContentBlockID>;
|
|
7
|
+
};
|
|
8
|
+
declare const initial: () => LifecycleState;
|
|
9
|
+
type Result = {
|
|
10
|
+
state: LifecycleState;
|
|
11
|
+
events: StreamEvent[];
|
|
12
|
+
};
|
|
13
|
+
declare const textDelta: (state: LifecycleState, events: StreamEvent[], id: ContentBlockID, text: string) => Result;
|
|
14
|
+
declare const reasoningDelta: (state: LifecycleState, events: StreamEvent[], id: ContentBlockID, text: string) => Result;
|
|
15
|
+
declare const closeOpenBlocks: (state: LifecycleState, events: StreamEvent[]) => void;
|
|
16
|
+
type FinishInput = {
|
|
17
|
+
reason: FinishReason;
|
|
18
|
+
usage?: Usage;
|
|
19
|
+
};
|
|
20
|
+
/** Close any open blocks, then emit a step-finish and finish event; reset stepStarted. */
|
|
21
|
+
declare const finish: (state: LifecycleState, events: StreamEvent[], input: FinishInput) => Result;
|
|
22
|
+
export type { FinishInput, LifecycleState, Result };
|
|
23
|
+
export { closeOpenBlocks, finish, initial, reasoningDelta, textDelta };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const initial = () => ({
|
|
2
|
+
stepStarted: false,
|
|
3
|
+
text: new Set(),
|
|
4
|
+
reasoning: new Set(),
|
|
5
|
+
});
|
|
6
|
+
const ensureStepStart = (state, events) => {
|
|
7
|
+
if (state.stepStarted)
|
|
8
|
+
return state;
|
|
9
|
+
events.push({ type: 'step-start', index: 0 });
|
|
10
|
+
return { ...state, stepStarted: true };
|
|
11
|
+
};
|
|
12
|
+
const textDelta = (state, events, id, text) => {
|
|
13
|
+
const withStep = ensureStepStart(state, events);
|
|
14
|
+
if (!withStep.text.has(id)) {
|
|
15
|
+
events.push({ type: 'text-start', id });
|
|
16
|
+
withStep.text.add(id);
|
|
17
|
+
}
|
|
18
|
+
events.push({ type: 'text-delta', id, text });
|
|
19
|
+
return { state: withStep, events };
|
|
20
|
+
};
|
|
21
|
+
const reasoningDelta = (state, events, id, text) => {
|
|
22
|
+
const withStep = ensureStepStart(state, events);
|
|
23
|
+
if (!withStep.reasoning.has(id)) {
|
|
24
|
+
events.push({ type: 'reasoning-start', id });
|
|
25
|
+
withStep.reasoning.add(id);
|
|
26
|
+
}
|
|
27
|
+
events.push({ type: 'reasoning-delta', id, text });
|
|
28
|
+
return { state: withStep, events };
|
|
29
|
+
};
|
|
30
|
+
const closeOpenBlocks = (state, events) => {
|
|
31
|
+
for (const id of state.reasoning)
|
|
32
|
+
events.push({ type: 'reasoning-end', id });
|
|
33
|
+
for (const id of state.text)
|
|
34
|
+
events.push({ type: 'text-end', id });
|
|
35
|
+
state.reasoning.clear();
|
|
36
|
+
state.text.clear();
|
|
37
|
+
};
|
|
38
|
+
/** Close any open blocks, then emit a step-finish and finish event; reset stepStarted. */
|
|
39
|
+
const finish = (state, events, input) => {
|
|
40
|
+
const withStep = ensureStepStart(state, events);
|
|
41
|
+
closeOpenBlocks(withStep, events);
|
|
42
|
+
events.push({ type: 'step-finish', index: 0, reason: input.reason, usage: input.usage });
|
|
43
|
+
events.push({ type: 'finish', reason: input.reason, usage: input.usage });
|
|
44
|
+
return { state: { ...withStep, stepStarted: false }, events };
|
|
45
|
+
};
|
|
46
|
+
export { closeOpenBlocks, finish, initial, reasoningDelta, textDelta };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { StreamEvent } from '../../schema/events.js';
|
|
2
|
+
import type { ToolCallID } from '../../schema/ids.js';
|
|
3
|
+
type PendingTool = {
|
|
4
|
+
id: ToolCallID;
|
|
5
|
+
name: string;
|
|
6
|
+
input: string;
|
|
7
|
+
started: boolean;
|
|
8
|
+
};
|
|
9
|
+
type ToolStreamState = Record<number, PendingTool>;
|
|
10
|
+
type DeltaInput = {
|
|
11
|
+
index: number;
|
|
12
|
+
id?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
/** Fragment of the JSON arguments string. */
|
|
15
|
+
argumentsDelta?: string;
|
|
16
|
+
};
|
|
17
|
+
type AppendOutcome = {
|
|
18
|
+
state: ToolStreamState;
|
|
19
|
+
events: StreamEvent[];
|
|
20
|
+
};
|
|
21
|
+
declare const empty: () => ToolStreamState;
|
|
22
|
+
/**
|
|
23
|
+
* Append a tool-call delta. If the index is new, emit a `tool-input-start`
|
|
24
|
+
* (using the delta's id/name if present, else synthesized). Then emit a
|
|
25
|
+
* `tool-input-delta` when an argument fragment is present.
|
|
26
|
+
*/
|
|
27
|
+
declare const appendOrStart: (state: ToolStreamState, delta: DeltaInput) => AppendOutcome;
|
|
28
|
+
type FinishedTool = {
|
|
29
|
+
id: ToolCallID;
|
|
30
|
+
name: string;
|
|
31
|
+
input: unknown;
|
|
32
|
+
};
|
|
33
|
+
type FinishAllOutcome = {
|
|
34
|
+
state: ToolStreamState;
|
|
35
|
+
events: StreamEvent[];
|
|
36
|
+
tools: FinishedTool[];
|
|
37
|
+
};
|
|
38
|
+
/** Parse a raw JSON arguments string; empty string becomes `{}`. */
|
|
39
|
+
declare const parseToolInput: (raw: string) => unknown;
|
|
40
|
+
/**
|
|
41
|
+
* Finalize all pending tool calls (OpenAI Chat style — no per-tool stop event).
|
|
42
|
+
* Emits `tool-input-end` + parsed `tool-call` for each, and clears state.
|
|
43
|
+
*/
|
|
44
|
+
declare const finishAll: (state: ToolStreamState) => FinishAllOutcome;
|
|
45
|
+
export type { AppendOutcome, DeltaInput, FinishAllOutcome, FinishedTool, ToolStreamState };
|
|
46
|
+
export { appendOrStart, empty, finishAll, parseToolInput };
|