@migor/agentia 0.0.2 → 0.2.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/README.md +62 -6
- package/dist/core/json.d.ts +2 -0
- package/dist/core/json.js +12 -0
- package/dist/core/json.js.map +1 -0
- package/dist/core/schema.d.ts +2 -1
- package/dist/core/schema.js +11 -9
- package/dist/core/schema.js.map +1 -1
- package/dist/core/tool.d.ts +19 -1
- package/dist/engine/loop.d.ts +11 -3
- package/dist/engine/loop.js +69 -15
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/replay.d.ts +31 -0
- package/dist/engine/replay.js +107 -0
- package/dist/engine/replay.js.map +1 -0
- package/dist/engine/types.d.ts +18 -4
- package/dist/index.d.ts +18 -2
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/run/async.d.ts +12 -17
- package/dist/run/async.js +66 -16
- package/dist/run/async.js.map +1 -1
- package/dist/run/http.d.ts +47 -0
- package/dist/run/http.js +119 -0
- package/dist/run/http.js.map +1 -0
- package/dist/run/memory.d.ts +18 -0
- package/dist/run/memory.js +18 -0
- package/dist/run/memory.js.map +1 -0
- package/dist/run/openai.d.ts +24 -0
- package/dist/run/openai.js +181 -0
- package/dist/run/openai.js.map +1 -0
- package/dist/run/otlp.d.ts +26 -0
- package/dist/run/otlp.js +105 -0
- package/dist/run/otlp.js.map +1 -0
- package/dist/run/redisStore.d.ts +54 -0
- package/dist/run/redisStore.js +72 -0
- package/dist/run/redisStore.js.map +1 -0
- package/dist/run/run.d.ts +10 -0
- package/dist/run/run.js +31 -0
- package/dist/run/run.js.map +1 -1
- package/dist/run/spec.d.ts +2 -1
- package/dist/run/spec.js.map +1 -1
- package/dist/run/sqliteStore.d.ts +24 -0
- package/dist/run/sqliteStore.js +60 -0
- package/dist/run/sqliteStore.js.map +1 -0
- package/dist/run/store.d.ts +12 -5
- package/dist/run/store.js.map +1 -1
- package/dist/toolkit/collect.d.ts +23 -0
- package/dist/toolkit/collect.js +45 -0
- package/dist/toolkit/collect.js.map +1 -0
- package/dist/toolkit/discover.js +7 -1
- package/dist/toolkit/discover.js.map +1 -1
- package/dist/toolkit/middleware.d.ts +27 -0
- package/dist/toolkit/middleware.js +15 -0
- package/dist/toolkit/middleware.js.map +1 -0
- package/dist/toolkit/module.d.ts +18 -0
- package/dist/toolkit/module.js +34 -1
- package/dist/toolkit/module.js.map +1 -1
- package/dist/toolkit/prompt.js +6 -20
- package/dist/toolkit/prompt.js.map +1 -1
- package/dist/toolkit/skill.js +8 -29
- package/dist/toolkit/skill.js.map +1 -1
- package/dist/toolkit/subagent.d.ts +6 -0
- package/dist/toolkit/subagent.js +16 -28
- package/dist/toolkit/subagent.js.map +1 -1
- package/dist/toolkit/tool.js +3 -25
- package/dist/toolkit/tool.js.map +1 -1
- package/dist/toolkit/zod.d.ts +26 -0
- package/dist/toolkit/zod.js +45 -0
- package/dist/toolkit/zod.js.map +1 -0
- package/package.json +27 -17
package/dist/engine/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import type { AgentTool } from '../core/tool.js';
|
|
2
|
+
import type { AgentTool, JsonSchema, ModelClient } from '../core/tool.js';
|
|
3
3
|
import type { SpanError, Trace } from '../core/trace.js';
|
|
4
|
+
export type { ModelClient } from '../core/tool.js';
|
|
4
5
|
export type AgentStopReason = 'end_turn' | 'max_tokens' | 'refusal' | 'pause_turn' | 'max_iterations' | 'tool_use_no_blocks' | 'error';
|
|
5
6
|
/** 可携带 cache_control 的 system 文本块(见 run/systemPrompt.ts) */
|
|
6
7
|
export interface SystemTextBlock {
|
|
@@ -25,6 +26,11 @@ export interface ContextPolicy {
|
|
|
25
26
|
model: string;
|
|
26
27
|
}): Promise<Anthropic.MessageParam[]>;
|
|
27
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* engine 对模型端的最小结构面(R4 多模型):Anthropic SDK 天然满足,
|
|
31
|
+
* 其他 provider(OpenAI 兼容端点等)只需适配出同一形态。
|
|
32
|
+
* 定义在 core/tool.js 并从此处转导出。
|
|
33
|
+
*/
|
|
28
34
|
export interface RunAgentOptions {
|
|
29
35
|
/** 顶层 system(SystemPrompt 产物)。稳定内容应放在 tools 之后、第一个 breakpoint 前 */
|
|
30
36
|
system?: SystemParam;
|
|
@@ -37,8 +43,8 @@ export interface RunAgentOptions {
|
|
|
37
43
|
maxTokens?: number;
|
|
38
44
|
/** 循环安全上限,防止无限 tool 往返 */
|
|
39
45
|
maxIterations?: number;
|
|
40
|
-
/** 注入 client(默认 new Anthropic(),读 env/ant auth
|
|
41
|
-
client?:
|
|
46
|
+
/** 注入 client(默认 new Anthropic(),读 env/ant auth);多模型见 ModelClient */
|
|
47
|
+
client?: ModelClient;
|
|
42
48
|
/** 注入 recorder(run 层复用;不注入则内部新建,traceId 即 runId) */
|
|
43
49
|
recorder?: import('./tracer.js').TraceRecorder;
|
|
44
50
|
/** 文本增量回调(终端/SSE 用) */
|
|
@@ -46,13 +52,21 @@ export interface RunAgentOptions {
|
|
|
46
52
|
runName?: string;
|
|
47
53
|
/** 上下文预算策略:每回合发送前可编辑/压缩消息(compaction / context editing) */
|
|
48
54
|
contextPolicy?: ContextPolicy;
|
|
55
|
+
/**
|
|
56
|
+
* 结构化结果 schema(R2):给出后 engine 追加隐藏工具 submit_result,
|
|
57
|
+
* 模型调用它提交符合 schema 的最终结果,校验通过即结束循环并写入 AgentRunResult.typed;
|
|
58
|
+
* 模型始终未提交则 typed 为 undefined(行为与不设时一致)。
|
|
59
|
+
*/
|
|
60
|
+
resultSchema?: JsonSchema;
|
|
49
61
|
}
|
|
50
62
|
export interface AgentRunResult {
|
|
51
63
|
/** 本次 run 的完整调用树 + usage(traceId == runId) */
|
|
52
64
|
trace: Trace;
|
|
53
65
|
stopReason: AgentStopReason;
|
|
54
|
-
/**
|
|
66
|
+
/** 最终文本(提交结构化结果的回合若带文本则取之,可空) */
|
|
55
67
|
finalText: string;
|
|
56
68
|
iterations: number;
|
|
57
69
|
error?: SpanError;
|
|
70
|
+
/** resultSchema 校验通过的结构化结果;模型没提交(或未设 resultSchema)则为 undefined */
|
|
71
|
+
typed?: unknown;
|
|
58
72
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export declare const AGENTIA_VERSION = "0.0
|
|
1
|
+
export declare const AGENTIA_VERSION = "0.2.0";
|
|
2
2
|
export type { Trace, Span, SpanEvent, SpanError, SpanId, SpanKind, SpanStatus, TraceId, UnitType, Usage, } from './core/trace.js';
|
|
3
|
-
export type { AgentTool, JsonSchema, RecorderBackend, ToolRunContext } from './core/tool.js';
|
|
3
|
+
export type { AgentTool, JsonSchema, ModelClient, RecorderBackend, ToolRunContext } from './core/tool.js';
|
|
4
4
|
export { validateJsonSchema } from './core/schema.js';
|
|
5
5
|
export { runAgent, resolveDefaultModel } from './engine/loop.js';
|
|
6
6
|
export { TraceRecorder } from './engine/tracer.js';
|
|
@@ -20,6 +20,8 @@ export { Container } from './container/container.js';
|
|
|
20
20
|
export type { Provider, ClassProvider, ValueProvider, FactoryProvider, Token, } from './container/container.js';
|
|
21
21
|
export { asset } from './toolkit/asset.js';
|
|
22
22
|
export { discoverProviders } from './toolkit/discover.js';
|
|
23
|
+
export { applyMiddleware } from './toolkit/middleware.js';
|
|
24
|
+
export type { UnitCall, UnitMiddleware, UnitNext } from './toolkit/middleware.js';
|
|
23
25
|
export { Tool, collectTools } from './toolkit/tool.js';
|
|
24
26
|
export type { ToolSpec } from './toolkit/tool.js';
|
|
25
27
|
export { SubAgent, collectSubAgents, subagentToTool } from './toolkit/subagent.js';
|
|
@@ -30,6 +32,9 @@ export { Prompt, collectPrompts } from './toolkit/prompt.js';
|
|
|
30
32
|
export type { PromptSpec } from './toolkit/prompt.js';
|
|
31
33
|
export { createApp, AgentApp } from './toolkit/module.js';
|
|
32
34
|
export type { AppOptions, RunAppOptions, AgentRunOutput } from './toolkit/module.js';
|
|
35
|
+
export { defineModule } from './toolkit/module.js';
|
|
36
|
+
export type { AgentModule } from './toolkit/module.js';
|
|
37
|
+
export { fromZod } from './toolkit/zod.js';
|
|
33
38
|
export { normalizeMessages } from './run/spec.js';
|
|
34
39
|
export type { RunInput, RunSpec, RunInvocationOptions } from './run/spec.js';
|
|
35
40
|
export { InMemoryTaskStore } from './run/store.js';
|
|
@@ -40,3 +45,14 @@ export type { AsyncRunnerOptions, AppCallable } from './run/async.js';
|
|
|
40
45
|
export { Scheduler } from './run/scheduler.js';
|
|
41
46
|
export type { ScheduleEveryOptions, ScheduleHandle } from './run/scheduler.js';
|
|
42
47
|
export { runSync, createSyncHandler } from './run/transport.js';
|
|
48
|
+
export { createHttpHandler } from './run/http.js';
|
|
49
|
+
export { SqliteTaskStore } from './run/sqliteStore.js';
|
|
50
|
+
export { createOtlpExporter } from './run/otlp.js';
|
|
51
|
+
export { createOpenAIClient } from './run/openai.js';
|
|
52
|
+
export { InMemoryMemoryStore } from './run/memory.js';
|
|
53
|
+
export type { MemoryStore } from './run/memory.js';
|
|
54
|
+
export { RedisTaskStore } from './run/redisStore.js';
|
|
55
|
+
export type { RedisLike } from './run/redisStore.js';
|
|
56
|
+
export type { MaybePromise } from './run/store.js';
|
|
57
|
+
export { traceToMessages } from './engine/replay.js';
|
|
58
|
+
export type { ReplayOptions } from './engine/replay.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const AGENTIA_VERSION = '0.0
|
|
1
|
+
export const AGENTIA_VERSION = '0.2.0';
|
|
2
2
|
export { validateJsonSchema } from './core/schema.js';
|
|
3
3
|
// engine:运行时内核
|
|
4
4
|
export { runAgent, resolveDefaultModel } from './engine/loop.js';
|
|
@@ -16,11 +16,14 @@ export { Container } from './container/container.js';
|
|
|
16
16
|
// toolkit:声明式单元 + 应用装配
|
|
17
17
|
export { asset } from './toolkit/asset.js';
|
|
18
18
|
export { discoverProviders } from './toolkit/discover.js';
|
|
19
|
+
export { applyMiddleware } from './toolkit/middleware.js';
|
|
19
20
|
export { Tool, collectTools } from './toolkit/tool.js';
|
|
20
21
|
export { SubAgent, collectSubAgents, subagentToTool } from './toolkit/subagent.js';
|
|
21
22
|
export { Skill, collectSkills, skillToTool } from './toolkit/skill.js';
|
|
22
23
|
export { Prompt, collectPrompts } from './toolkit/prompt.js';
|
|
23
24
|
export { createApp, AgentApp } from './toolkit/module.js';
|
|
25
|
+
export { defineModule } from './toolkit/module.js';
|
|
26
|
+
export { fromZod } from './toolkit/zod.js';
|
|
24
27
|
// run:触发传输 + run 存储
|
|
25
28
|
export { normalizeMessages } from './run/spec.js';
|
|
26
29
|
export { InMemoryTaskStore } from './run/store.js';
|
|
@@ -28,4 +31,12 @@ export { FileTaskStore } from './run/fsStore.js';
|
|
|
28
31
|
export { AsyncRunner } from './run/async.js';
|
|
29
32
|
export { Scheduler } from './run/scheduler.js';
|
|
30
33
|
export { runSync, createSyncHandler } from './run/transport.js';
|
|
34
|
+
// run:宿主与导出(R3/R4)
|
|
35
|
+
export { createHttpHandler } from './run/http.js';
|
|
36
|
+
export { SqliteTaskStore } from './run/sqliteStore.js';
|
|
37
|
+
export { createOtlpExporter } from './run/otlp.js';
|
|
38
|
+
export { createOpenAIClient } from './run/openai.js';
|
|
39
|
+
export { InMemoryMemoryStore } from './run/memory.js';
|
|
40
|
+
export { RedisTaskStore } from './run/redisStore.js';
|
|
41
|
+
export { traceToMessages } from './engine/replay.js';
|
|
31
42
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AAgBvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAe;AACf,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAUnD,gBAAgB;AAChB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,eAAe;AACf,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAKrD,kBAAkB;AAClB,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AASrD,uBAAuB;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEnF,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAQvE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AAgBvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAe;AACf,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAUnD,gBAAgB;AAChB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD,eAAe;AACf,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAKrD,kBAAkB;AAClB,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AASrD,uBAAuB;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEnF,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAQvE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,oBAAoB;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEhE,mBAAmB;AACnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/run/async.d.ts
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import type Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import type { ModelClient } from '../core/tool.js';
|
|
2
3
|
import type { AgentRunResult } from '../engine/types.js';
|
|
3
4
|
import type { RunStatus } from './types.js';
|
|
4
5
|
import type { RunInvocationOptions } from './spec.js';
|
|
5
|
-
import type { TaskRecord, TaskStore } from './store.js';
|
|
6
|
-
/**
|
|
7
|
-
* Agentia —— 异步任务宿主(spec §6.3 异步 / §6.5 确定性 / §6.6 换宿主不换语义)。
|
|
8
|
-
*
|
|
9
|
-
* - submit 即回(queued),后台驱动状态机 queued → running → succeeded/failed;
|
|
10
|
-
* - **at-least-once 去重**:同一 idempotencyKey 重复 submit,若上一任务仍在
|
|
11
|
-
* queued/running/succeeded 则直接返回既有记录,不重复执行(失败可重试新任务);
|
|
12
|
-
* - run 记录落 TaskStore(v1 InMemoryTaskStore),trace 随 result 一同保留;
|
|
13
|
-
* DB/队列宿主只需实现 TaskStore。
|
|
14
|
-
*/
|
|
6
|
+
import type { MaybePromise, TaskRecord, TaskStore } from './store.js';
|
|
15
7
|
export interface AsyncRunnerOptions {
|
|
16
|
-
client?:
|
|
8
|
+
client?: ModelClient;
|
|
17
9
|
store?: TaskStore;
|
|
18
10
|
/** 同时执行的任务上限;缺省不限。超出部分排队等槽位(状态保持 queued) */
|
|
19
11
|
concurrency?: number;
|
|
@@ -41,16 +33,18 @@ export declare class AsyncRunner {
|
|
|
41
33
|
/**
|
|
42
34
|
* 提交一次异步任务。入参可为 string / messages / {prompt|text|messages}。
|
|
43
35
|
* 幂等键存在且上一任务未失败 → 直接返回既有记录(去重);失败的同键可产生新任务。
|
|
36
|
+
* 同步门面:同步 store 下立即去重并返回快照;异步 store 下 save 在后台完成、
|
|
37
|
+
* 去重推迟到执行前(见 #execute),返回新建任务的提交时刻快照。
|
|
44
38
|
*/
|
|
45
39
|
submit(input: unknown, opts?: {
|
|
46
40
|
idempotencyKey?: string;
|
|
47
41
|
source?: string;
|
|
48
42
|
options?: RunInvocationOptions;
|
|
49
43
|
}): TaskRecord;
|
|
50
|
-
/**
|
|
51
|
-
poll(taskId: string): TaskRecord | undefined
|
|
52
|
-
byIdempotency(key: string): TaskRecord | undefined
|
|
53
|
-
list(): TaskRecord[]
|
|
44
|
+
/** 查任务当前记录(异步 store 下返回 Promise,调用方 await) */
|
|
45
|
+
poll(taskId: string): MaybePromise<TaskRecord | undefined>;
|
|
46
|
+
byIdempotency(key: string): MaybePromise<TaskRecord | undefined>;
|
|
47
|
+
list(): MaybePromise<TaskRecord[]>;
|
|
54
48
|
/** 等到任务终态;超时抛错。 */
|
|
55
49
|
awaitTask(taskId: string, opts?: {
|
|
56
50
|
timeoutMs?: number;
|
|
@@ -58,7 +52,8 @@ export declare class AsyncRunner {
|
|
|
58
52
|
}): Promise<TaskRecord>;
|
|
59
53
|
/**
|
|
60
54
|
* 宿主重启续跑:把 store 里 queued | running 的记录重新派发执行
|
|
61
|
-
* (running
|
|
55
|
+
* (running 视为进程中断)。返回重派数量(异步 store 下返回 Promise<number>)。
|
|
56
|
+
* 幂等键去重照常生效。
|
|
62
57
|
*/
|
|
63
|
-
resumePending(): number
|
|
58
|
+
resumePending(): number | Promise<number>;
|
|
64
59
|
}
|
package/dist/run/async.js
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { classifyError } from '../engine/errors.js';
|
|
2
2
|
import { normalizeMessages } from './spec.js';
|
|
3
3
|
import { InMemoryTaskStore } from './store.js';
|
|
4
|
+
/**
|
|
5
|
+
* Agentia —— 异步任务宿主(spec §6.3 异步 / §6.5 确定性 / §6.6 换宿主不换语义)。
|
|
6
|
+
*
|
|
7
|
+
* - submit 即回(queued),后台驱动状态机 queued → running → succeeded/failed;
|
|
8
|
+
* - **at-least-once 去重**:同一 idempotencyKey 重复 submit,若上一任务仍在
|
|
9
|
+
* queued/running/succeeded 则直接返回既有记录,不重复执行(失败可重试新任务);
|
|
10
|
+
* - run 记录落 TaskStore(v1 InMemoryTaskStore),trace 随 result 一同保留;
|
|
11
|
+
* DB/队列宿主只需实现 TaskStore。
|
|
12
|
+
*
|
|
13
|
+
* 异步 TaskStore(R6,TaskStore 方法返回 MaybePromise):
|
|
14
|
+
* - 内部执行路径(#execute / awaitTask / resumePending)全部 await 化,两种 store 通吃;
|
|
15
|
+
* - submit/poll/byIdempotency/list 是**同步门面**,为同步 store 保持原有用法与返回类型
|
|
16
|
+
* (http/scheduler/既有调用方零改动)。接异步 store 时:submit 的即时去重无法进行
|
|
17
|
+
* (推迟到 #execute:同键已有 succeeded 记录则采纳其结果、不重复执行),poll 等
|
|
18
|
+
* 返回 Promise 需调用方自行 await。
|
|
19
|
+
*/
|
|
20
|
+
/** store 返回值可能是同步值或 Promise —— 区分用(同步门面只在同步值上工作) */
|
|
21
|
+
function isThenable(x) {
|
|
22
|
+
return !!x && typeof x.then === 'function';
|
|
23
|
+
}
|
|
4
24
|
export class AsyncRunner {
|
|
5
25
|
app;
|
|
6
26
|
store;
|
|
@@ -21,12 +41,15 @@ export class AsyncRunner {
|
|
|
21
41
|
/**
|
|
22
42
|
* 提交一次异步任务。入参可为 string / messages / {prompt|text|messages}。
|
|
23
43
|
* 幂等键存在且上一任务未失败 → 直接返回既有记录(去重);失败的同键可产生新任务。
|
|
44
|
+
* 同步门面:同步 store 下立即去重并返回快照;异步 store 下 save 在后台完成、
|
|
45
|
+
* 去重推迟到执行前(见 #execute),返回新建任务的提交时刻快照。
|
|
24
46
|
*/
|
|
25
47
|
submit(input, opts = {}) {
|
|
26
48
|
const messages = normalizeMessages(input);
|
|
27
49
|
if (opts.idempotencyKey) {
|
|
28
50
|
const existing = this.store.byIdempotency(opts.idempotencyKey);
|
|
29
|
-
|
|
51
|
+
// 异步 store 返回 Promise —— 同步门面无法 await,去重交给 #execute
|
|
52
|
+
if (existing && !isThenable(existing) && existing.status !== 'failed') {
|
|
30
53
|
return { ...existing }; // at-least-once 去重:不重复执行
|
|
31
54
|
}
|
|
32
55
|
}
|
|
@@ -37,12 +60,21 @@ export class AsyncRunner {
|
|
|
37
60
|
spec: { messages, options: opts.options, source: opts.source ?? 'async' },
|
|
38
61
|
createdAt: Date.now(),
|
|
39
62
|
};
|
|
40
|
-
this.store.save(rec);
|
|
41
|
-
|
|
63
|
+
const saved = this.store.save(rec);
|
|
64
|
+
if (isThenable(saved)) {
|
|
65
|
+
// 异步落库失败:尽力把任务标记为 failed 重存,避免静默吞错 / unhandled rejection
|
|
66
|
+
saved.catch((e) => {
|
|
67
|
+
rec.status = 'failed';
|
|
68
|
+
rec.error = classifyError(e);
|
|
69
|
+
rec.finishedAt = Date.now();
|
|
70
|
+
void Promise.resolve(this.store.save(rec)).catch(() => { });
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
void this.#execute(rec);
|
|
42
74
|
// 返回浅拷贝:记录会被后台状态机原地推进,调用方拿到的是提交时刻的快照
|
|
43
75
|
return { ...rec };
|
|
44
76
|
}
|
|
45
|
-
/**
|
|
77
|
+
/** 查任务当前记录(异步 store 下返回 Promise,调用方 await) */
|
|
46
78
|
poll(taskId) {
|
|
47
79
|
return this.store.get(taskId);
|
|
48
80
|
}
|
|
@@ -58,7 +90,7 @@ export class AsyncRunner {
|
|
|
58
90
|
const intervalMs = opts.intervalMs ?? 5;
|
|
59
91
|
const start = Date.now();
|
|
60
92
|
for (;;) {
|
|
61
|
-
const rec = this.store.get(taskId);
|
|
93
|
+
const rec = await this.store.get(taskId);
|
|
62
94
|
if (!rec)
|
|
63
95
|
throw new Error(`task 不存在: ${taskId}`);
|
|
64
96
|
if (rec.status === 'succeeded' || rec.status === 'failed')
|
|
@@ -71,26 +103,44 @@ export class AsyncRunner {
|
|
|
71
103
|
}
|
|
72
104
|
/**
|
|
73
105
|
* 宿主重启续跑:把 store 里 queued | running 的记录重新派发执行
|
|
74
|
-
* (running
|
|
106
|
+
* (running 视为进程中断)。返回重派数量(异步 store 下返回 Promise<number>)。
|
|
107
|
+
* 幂等键去重照常生效。
|
|
75
108
|
*/
|
|
76
109
|
resumePending() {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
.
|
|
110
|
+
const listed = this.store.list();
|
|
111
|
+
if (isThenable(listed))
|
|
112
|
+
return listed.then((recs) => this.#redispatch(recs));
|
|
113
|
+
return this.#redispatch(listed);
|
|
114
|
+
}
|
|
115
|
+
#redispatch(recs) {
|
|
116
|
+
const pending = recs.filter((r) => r.status === 'queued' || r.status === 'running');
|
|
80
117
|
for (const rec of pending) {
|
|
81
118
|
rec.status = 'queued'; // 重新入队,由 #execute 统一推进
|
|
82
|
-
void this.#execute(rec
|
|
119
|
+
void this.#execute(rec);
|
|
83
120
|
}
|
|
84
121
|
return pending.length;
|
|
85
122
|
}
|
|
86
|
-
async #execute(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
123
|
+
async #execute(rec) {
|
|
124
|
+
// 异步 store 的幂等去重在此补齐(submit 同步门面无法 await):
|
|
125
|
+
// 同键已有 succeeded 记录 → 采纳其结果,不重复执行;queued/running 不采纳 ——
|
|
126
|
+
// 重复执行本就是 at-least-once 允许的行为。同步 store 下 submit 已完成去重,
|
|
127
|
+
// 这里查到的一般是 rec 自身(taskId 相同,直接放行)。
|
|
128
|
+
if (rec.idempotencyKey) {
|
|
129
|
+
const existing = await this.store.byIdempotency(rec.idempotencyKey);
|
|
130
|
+
if (existing && existing.taskId !== rec.taskId && existing.status === 'succeeded') {
|
|
131
|
+
rec.status = 'succeeded';
|
|
132
|
+
rec.runId = existing.runId;
|
|
133
|
+
rec.result = existing.result;
|
|
134
|
+
rec.error = existing.error;
|
|
135
|
+
rec.finishedAt = Date.now();
|
|
136
|
+
await this.store.save(rec);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
90
140
|
await this.#acquireSlot();
|
|
91
141
|
rec.status = 'running';
|
|
92
142
|
rec.startedAt = Date.now();
|
|
93
|
-
this.store.save(rec);
|
|
143
|
+
await this.store.save(rec);
|
|
94
144
|
try {
|
|
95
145
|
const callOpts = {
|
|
96
146
|
...(rec.spec.options ?? {}),
|
|
@@ -110,7 +160,7 @@ export class AsyncRunner {
|
|
|
110
160
|
}
|
|
111
161
|
finally {
|
|
112
162
|
rec.finishedAt = Date.now();
|
|
113
|
-
this.store.save(rec);
|
|
163
|
+
await this.store.save(rec);
|
|
114
164
|
this.#releaseSlot();
|
|
115
165
|
}
|
|
116
166
|
}
|
package/dist/run/async.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/run/async.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/run/async.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { SpanError, Trace } from '../core/trace.js';
|
|
3
|
+
import type { AgentStopReason } from '../engine/types.js';
|
|
4
|
+
import { AsyncRunner } from './async.js';
|
|
5
|
+
import type { AppCallable } from './async.js';
|
|
6
|
+
import type { RunInvocationOptions } from './spec.js';
|
|
7
|
+
import type { RunStatus } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Agentia —— HTTP 宿主(spec §6.6:换宿主不换语义,roadmap R3)。
|
|
10
|
+
*
|
|
11
|
+
* 只产出一个 (req, res) handler,不做 listen —— 交给用户
|
|
12
|
+
* `http.createServer(createHttpHandler(app)).listen(...)`,可挂进任意 Node HTTP 框架。
|
|
13
|
+
*
|
|
14
|
+
* 端点契约(全部 JSON):
|
|
15
|
+
* - POST /run 同步 RPC。body = RunInput(string / messages / {prompt|text|messages}),
|
|
16
|
+
* 走 normalizeMessages 规整后同步执行;200 { runId, status, stopReason,
|
|
17
|
+
* finalText, typed?, trace, error? }。status=failed 也照返 200
|
|
18
|
+
* (rethrow:false 语义:硬失败以 error 字段返回,不用 HTTP 错误码表达);
|
|
19
|
+
* 输入无法规整为 messages → 400 { error }。
|
|
20
|
+
* - POST /tasks 异步任务。body { input, idempotencyKey?, options? } →
|
|
21
|
+
* AsyncRunner.submit → 202 TaskRecord(queued,幂等键去重照常生效)。
|
|
22
|
+
* - GET /tasks/<id> 轮询任务记录 → 200 TaskRecord;不存在 → 404。
|
|
23
|
+
*
|
|
24
|
+
* 方法不符 405;路径不符 404;body 非法 JSON 400。runner 缺省内部 new AsyncRunner(app)。
|
|
25
|
+
*/
|
|
26
|
+
/** POST /run 的响应形态 */
|
|
27
|
+
export interface RunHttpResponse {
|
|
28
|
+
runId: string;
|
|
29
|
+
status: RunStatus;
|
|
30
|
+
stopReason: AgentStopReason;
|
|
31
|
+
finalText: string;
|
|
32
|
+
/** 结构化结果(R2 起应用可携带;无则省略) */
|
|
33
|
+
typed?: unknown;
|
|
34
|
+
trace: Trace;
|
|
35
|
+
error?: SpanError;
|
|
36
|
+
}
|
|
37
|
+
/** POST /tasks 的请求体形态 */
|
|
38
|
+
export interface TaskSubmitBody {
|
|
39
|
+
input: unknown;
|
|
40
|
+
idempotencyKey?: string;
|
|
41
|
+
options?: RunInvocationOptions;
|
|
42
|
+
}
|
|
43
|
+
export interface HttpHandlerOptions {
|
|
44
|
+
/** 异步任务宿主;缺省 new AsyncRunner(app)(InMemoryTaskStore) */
|
|
45
|
+
runner?: AsyncRunner;
|
|
46
|
+
}
|
|
47
|
+
export declare function createHttpHandler(app: AppCallable, opts?: HttpHandlerOptions): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
package/dist/run/http.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { AsyncRunner } from './async.js';
|
|
2
|
+
import { normalizeMessages } from './spec.js';
|
|
3
|
+
function sendJson(res, status, body) {
|
|
4
|
+
const payload = JSON.stringify(body);
|
|
5
|
+
res.writeHead(status, {
|
|
6
|
+
'content-type': 'application/json; charset=utf-8',
|
|
7
|
+
'content-length': Buffer.byteLength(payload),
|
|
8
|
+
});
|
|
9
|
+
res.end(payload);
|
|
10
|
+
}
|
|
11
|
+
function readBody(req) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const chunks = [];
|
|
14
|
+
req.on('data', (c) => chunks.push(c));
|
|
15
|
+
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
16
|
+
req.on('error', reject);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function errMessage(e) {
|
|
20
|
+
return e instanceof Error ? e.message : String(e);
|
|
21
|
+
}
|
|
22
|
+
export function createHttpHandler(app, opts = {}) {
|
|
23
|
+
const runner = opts.runner ?? new AsyncRunner(app);
|
|
24
|
+
return async (req, res) => {
|
|
25
|
+
const method = req.method ?? 'GET';
|
|
26
|
+
const pathname = (req.url ?? '/').split('?')[0];
|
|
27
|
+
try {
|
|
28
|
+
if (pathname === '/run') {
|
|
29
|
+
if (method !== 'POST') {
|
|
30
|
+
sendJson(res, 405, { error: `method ${method} not allowed, use POST` });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const input = await parseJsonBody(req, res);
|
|
34
|
+
if (input === PARSE_FAILED)
|
|
35
|
+
return;
|
|
36
|
+
let messages;
|
|
37
|
+
try {
|
|
38
|
+
messages = normalizeMessages(input);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
sendJson(res, 400, { error: errMessage(e) });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// rethrow:false —— 与 AsyncRunner 对齐:硬失败也以 status/error 字段返回 200
|
|
45
|
+
const out = await app.run(messages, { rethrow: false });
|
|
46
|
+
const body = {
|
|
47
|
+
runId: out.run.runId,
|
|
48
|
+
status: out.run.status,
|
|
49
|
+
stopReason: out.result.stopReason,
|
|
50
|
+
finalText: out.result.finalText,
|
|
51
|
+
typed: out.result.typed,
|
|
52
|
+
trace: out.result.trace,
|
|
53
|
+
error: out.result.error,
|
|
54
|
+
};
|
|
55
|
+
sendJson(res, 200, body);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (pathname === '/tasks') {
|
|
59
|
+
if (method !== 'POST') {
|
|
60
|
+
sendJson(res, 405, { error: `method ${method} not allowed, use POST` });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const body = await parseJsonBody(req, res);
|
|
64
|
+
if (body === PARSE_FAILED)
|
|
65
|
+
return;
|
|
66
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
67
|
+
sendJson(res, 400, { error: 'body 需为 { input, idempotencyKey?, options? }' });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const submitBody = body;
|
|
71
|
+
let rec;
|
|
72
|
+
try {
|
|
73
|
+
rec = runner.submit(submitBody.input, {
|
|
74
|
+
idempotencyKey: submitBody.idempotencyKey,
|
|
75
|
+
options: submitBody.options,
|
|
76
|
+
source: 'http',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
sendJson(res, 400, { error: errMessage(e) });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
sendJson(res, 202, rec);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (pathname.startsWith('/tasks/')) {
|
|
87
|
+
if (method !== 'GET') {
|
|
88
|
+
sendJson(res, 405, { error: `method ${method} not allowed, use GET` });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const taskId = decodeURIComponent(pathname.slice('/tasks/'.length));
|
|
92
|
+
const rec = runner.poll(taskId);
|
|
93
|
+
if (!rec) {
|
|
94
|
+
sendJson(res, 404, { error: `task 不存在: ${taskId}` });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
sendJson(res, 200, rec);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
sendJson(res, 404, { error: `路径不存在: ${pathname}` });
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
sendJson(res, 500, { error: errMessage(e) });
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const PARSE_FAILED = Symbol('parse-failed');
|
|
108
|
+
/** 读取并解析 JSON body;失败时直接回 400 并返回哨兵。 */
|
|
109
|
+
async function parseJsonBody(req, res) {
|
|
110
|
+
const raw = await readBody(req);
|
|
111
|
+
try {
|
|
112
|
+
return raw ? JSON.parse(raw) : undefined;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
sendJson(res, 400, { error: '请求 body 不是合法 JSON' });
|
|
116
|
+
return PARSE_FAILED;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/run/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryStore —— 跨 run 记忆(R4)。
|
|
3
|
+
*
|
|
4
|
+
* blackboard(RunContext)随单次 run 释放;MemoryStore 在 run 边界做
|
|
5
|
+
* 水合/回写:run 开始时把 load(keys) 注入 blackboard,run 结束时把
|
|
6
|
+
* 这些 key 的当前值 save 回 store —— 同一份 store 跨多次 executeRun
|
|
7
|
+
* 复用即得跨 run 记忆。接线见 run/run.ts 的 ExecuteRunOptions.memory。
|
|
8
|
+
*/
|
|
9
|
+
export interface MemoryStore {
|
|
10
|
+
load(keys: string[]): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
11
|
+
save(entries: Record<string, unknown>): void | Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
/** Map 实现:测试与缺省场景用(进程内,无持久化) */
|
|
14
|
+
export declare class InMemoryMemoryStore implements MemoryStore {
|
|
15
|
+
private readonly data;
|
|
16
|
+
load(keys: string[]): Record<string, unknown>;
|
|
17
|
+
save(entries: Record<string, unknown>): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Map 实现:测试与缺省场景用(进程内,无持久化) */
|
|
2
|
+
export class InMemoryMemoryStore {
|
|
3
|
+
data = new Map();
|
|
4
|
+
load(keys) {
|
|
5
|
+
const out = {};
|
|
6
|
+
for (const key of keys) {
|
|
7
|
+
if (this.data.has(key))
|
|
8
|
+
out[key] = this.data.get(key);
|
|
9
|
+
}
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
save(entries) {
|
|
13
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
14
|
+
this.data.set(key, value);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/run/memory.ts"],"names":[],"mappings":"AAaA,gCAAgC;AAChC,MAAM,OAAO,mBAAmB;IACb,IAAI,GAAG,IAAI,GAAG,EAAmB,CAAC;IAEnD,IAAI,CAAC,IAAc;QACjB,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,CAAC,OAAgC;QACnC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ModelClient } from '../core/tool.js';
|
|
2
|
+
/**
|
|
3
|
+
* OpenAI 兼容端点适配器(R4 多模型)。
|
|
4
|
+
*
|
|
5
|
+
* 把 engine 的 Anthropic 形态请求翻译成 OpenAI chat.completions 请求,
|
|
6
|
+
* 响应再翻译回 Anthropic.Message —— 产出的 ModelClient 可直接喂给
|
|
7
|
+
* executeRun / runAgent 的 client 选项。兼容端点(DeepSeek 等)换 baseURL 即可。
|
|
8
|
+
*
|
|
9
|
+
* 边界(近似映射,头部声明):
|
|
10
|
+
* - 非流式模拟:底层一次性 POST(stream:false),on('text') 回调在
|
|
11
|
+
* finalMessage() 完成后收到一次完整文本,而非逐 token 增量;
|
|
12
|
+
* - cache token 恒 0:OpenAI 形态无 prompt cache 计量字段;
|
|
13
|
+
* - refusal 为近似:finish_reason=content_filter 映射为 'refusal',
|
|
14
|
+
* 语义上接近但不等同 Anthropic 的流式分类器干预。
|
|
15
|
+
*/
|
|
16
|
+
export interface OpenAIClientOptions {
|
|
17
|
+
/** 缺省读 env OPENAI_API_KEY */
|
|
18
|
+
apiKey?: string;
|
|
19
|
+
/** 缺省 https://api.openai.com;兼容端点直接替换(如 https://api.deepseek.com) */
|
|
20
|
+
baseURL?: string;
|
|
21
|
+
/** 测试注入用 */
|
|
22
|
+
fetchImpl?: typeof fetch;
|
|
23
|
+
}
|
|
24
|
+
export declare function createOpenAIClient(opts?: OpenAIClientOptions): ModelClient;
|