@alpacakit/agents 0.1.0-beta.19
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 +224 -0
- package/dist/agents/builtin.d.ts +19 -0
- package/dist/agents/builtin.d.ts.map +1 -0
- package/dist/agents/builtin.js +14 -0
- package/dist/agents/claude.d.ts +20 -0
- package/dist/agents/claude.d.ts.map +1 -0
- package/dist/agents/claude.js +480 -0
- package/dist/agents/codex.d.ts +19 -0
- package/dist/agents/codex.d.ts.map +1 -0
- package/dist/agents/codex.js +355 -0
- package/dist/agents/openai-compatible/config.d.ts +16 -0
- package/dist/agents/openai-compatible/config.d.ts.map +1 -0
- package/dist/agents/openai-compatible/config.js +160 -0
- package/dist/agents/openai-compatible/engine.d.ts +18 -0
- package/dist/agents/openai-compatible/engine.d.ts.map +1 -0
- package/dist/agents/openai-compatible/engine.js +10 -0
- package/dist/agents/openai-compatible/fetch-engine.d.ts +38 -0
- package/dist/agents/openai-compatible/fetch-engine.d.ts.map +1 -0
- package/dist/agents/openai-compatible/fetch-engine.js +216 -0
- package/dist/agents/openai-compatible/pi/engine.d.ts +32 -0
- package/dist/agents/openai-compatible/pi/engine.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/engine.js +98 -0
- package/dist/agents/openai-compatible/pi/module.d.ts +87 -0
- package/dist/agents/openai-compatible/pi/module.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/module.js +43 -0
- package/dist/agents/openai-compatible/pi/sandbox.d.ts +25 -0
- package/dist/agents/openai-compatible/pi/sandbox.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/sandbox.js +69 -0
- package/dist/agents/openai-compatible/pi/session.d.ts +17 -0
- package/dist/agents/openai-compatible/pi/session.d.ts.map +1 -0
- package/dist/agents/openai-compatible/pi/session.js +354 -0
- package/dist/agents/openai-compatible/response.d.ts +19 -0
- package/dist/agents/openai-compatible/response.d.ts.map +1 -0
- package/dist/agents/openai-compatible/response.js +203 -0
- package/dist/agents/openai-compatible/types.d.ts +34 -0
- package/dist/agents/openai-compatible/types.d.ts.map +1 -0
- package/dist/agents/openai-compatible/types.js +1 -0
- package/dist/agents/openai-compatible.d.ts +12 -0
- package/dist/agents/openai-compatible.d.ts.map +1 -0
- package/dist/agents/openai-compatible.js +45 -0
- package/dist/auth/login-probe.d.ts +21 -0
- package/dist/auth/login-probe.d.ts.map +1 -0
- package/dist/auth/login-probe.js +47 -0
- package/dist/auth/login.d.ts +79 -0
- package/dist/auth/login.d.ts.map +1 -0
- package/dist/auth/login.js +9 -0
- package/dist/auth/material.d.ts +87 -0
- package/dist/auth/material.d.ts.map +1 -0
- package/dist/auth/material.js +59 -0
- package/dist/auth/profile-store.d.ts +14 -0
- package/dist/auth/profile-store.d.ts.map +1 -0
- package/dist/auth/profile-store.js +101 -0
- package/dist/auth/secrets.d.ts +14 -0
- package/dist/auth/secrets.d.ts.map +1 -0
- package/dist/auth/secrets.js +40 -0
- package/dist/codex/app-server.d.ts +30 -0
- package/dist/codex/app-server.d.ts.map +1 -0
- package/dist/codex/app-server.js +322 -0
- package/dist/codex/index.d.ts +3 -0
- package/dist/codex/index.d.ts.map +1 -0
- package/dist/codex/index.js +1 -0
- package/dist/codex/protocol.d.ts +124 -0
- package/dist/codex/protocol.d.ts.map +1 -0
- package/dist/codex/protocol.js +283 -0
- package/dist/config/agent-config-validation.d.ts +22 -0
- package/dist/config/agent-config-validation.d.ts.map +1 -0
- package/dist/config/agent-config-validation.js +31 -0
- package/dist/config/agent-configs.d.ts +37 -0
- package/dist/config/agent-configs.d.ts.map +1 -0
- package/dist/config/agent-configs.js +214 -0
- package/dist/config/compose.d.ts +10 -0
- package/dist/config/compose.d.ts.map +1 -0
- package/dist/config/compose.js +89 -0
- package/dist/config/config.d.ts +21 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +33 -0
- package/dist/config/errors.d.ts +45 -0
- package/dist/config/errors.d.ts.map +1 -0
- package/dist/config/errors.js +37 -0
- package/dist/config/extension.d.ts +3 -0
- package/dist/config/extension.d.ts.map +1 -0
- package/dist/config/extension.js +1 -0
- package/dist/config/ids.d.ts +8 -0
- package/dist/config/ids.d.ts.map +1 -0
- package/dist/config/ids.js +4 -0
- package/dist/config/index.d.ts +12 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/model.d.ts +105 -0
- package/dist/config/model.d.ts.map +1 -0
- package/dist/config/model.js +1 -0
- package/dist/config/paths.d.ts +7 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +10 -0
- package/dist/config/schema.d.ts +59 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +83 -0
- package/dist/config/store.d.ts +34 -0
- package/dist/config/store.d.ts.map +1 -0
- package/dist/config/store.js +420 -0
- package/dist/config/support.d.ts +14 -0
- package/dist/config/support.d.ts.map +1 -0
- package/dist/config/support.js +32 -0
- package/dist/config/values.d.ts +21 -0
- package/dist/config/values.d.ts.map +1 -0
- package/dist/config/values.js +22 -0
- package/dist/config/xdg.d.ts +20 -0
- package/dist/config/xdg.d.ts.map +1 -0
- package/dist/config/xdg.js +23 -0
- package/dist/core/async-queue.d.ts +14 -0
- package/dist/core/async-queue.d.ts.map +1 -0
- package/dist/core/async-queue.js +46 -0
- package/dist/core/env.d.ts +19 -0
- package/dist/core/env.d.ts.map +1 -0
- package/dist/core/env.js +39 -0
- package/dist/core/error.d.ts +121 -0
- package/dist/core/error.d.ts.map +1 -0
- package/dist/core/error.js +88 -0
- package/dist/core/fs.d.ts +16 -0
- package/dist/core/fs.d.ts.map +1 -0
- package/dist/core/fs.js +30 -0
- package/dist/core/json.d.ts +20 -0
- package/dist/core/json.d.ts.map +1 -0
- package/dist/core/json.js +42 -0
- package/dist/core/node-ports.d.ts +9 -0
- package/dist/core/node-ports.d.ts.map +1 -0
- package/dist/core/node-ports.js +135 -0
- package/dist/core/ports.d.ts +71 -0
- package/dist/core/ports.d.ts.map +1 -0
- package/dist/core/ports.js +19 -0
- package/dist/core/redact.d.ts +8 -0
- package/dist/core/redact.d.ts.map +1 -0
- package/dist/core/redact.js +66 -0
- package/dist/core/run.d.ts +125 -0
- package/dist/core/run.d.ts.map +1 -0
- package/dist/core/run.js +27 -0
- package/dist/core/values.d.ts +12 -0
- package/dist/core/values.d.ts.map +1 -0
- package/dist/core/values.js +9 -0
- package/dist/corpus/failure-corpus.d.ts +21 -0
- package/dist/corpus/failure-corpus.d.ts.map +1 -0
- package/dist/corpus/failure-corpus.js +89 -0
- package/dist/fleet/fleet.d.ts +120 -0
- package/dist/fleet/fleet.d.ts.map +1 -0
- package/dist/fleet/fleet.js +409 -0
- package/dist/fleet/policy.d.ts +45 -0
- package/dist/fleet/policy.d.ts.map +1 -0
- package/dist/fleet/policy.js +50 -0
- package/dist/fleet/store.d.ts +28 -0
- package/dist/fleet/store.d.ts.map +1 -0
- package/dist/fleet/store.js +225 -0
- package/dist/host/capabilities.d.ts +146 -0
- package/dist/host/capabilities.d.ts.map +1 -0
- package/dist/host/capabilities.js +59 -0
- package/dist/host/context.d.ts +29 -0
- package/dist/host/context.d.ts.map +1 -0
- package/dist/host/context.js +11 -0
- package/dist/host/github-dir.d.ts +16 -0
- package/dist/host/github-dir.d.ts.map +1 -0
- package/dist/host/github-dir.js +164 -0
- package/dist/host/mcp-list.d.ts +12 -0
- package/dist/host/mcp-list.d.ts.map +1 -0
- package/dist/host/mcp-list.js +122 -0
- package/dist/host/requirement-ref.d.ts +12 -0
- package/dist/host/requirement-ref.d.ts.map +1 -0
- package/dist/host/requirement-ref.js +40 -0
- package/dist/host/run-command.d.ts +27 -0
- package/dist/host/run-command.d.ts.map +1 -0
- package/dist/host/run-command.js +66 -0
- package/dist/host/validate-spec.d.ts +10 -0
- package/dist/host/validate-spec.d.ts.map +1 -0
- package/dist/host/validate-spec.js +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/kernel/classify.d.ts +44 -0
- package/dist/kernel/classify.d.ts.map +1 -0
- package/dist/kernel/classify.js +136 -0
- package/dist/kernel/cli-agent.d.ts +62 -0
- package/dist/kernel/cli-agent.d.ts.map +1 -0
- package/dist/kernel/cli-agent.js +281 -0
- package/dist/kernel/cli-login.d.ts +13 -0
- package/dist/kernel/cli-login.d.ts.map +1 -0
- package/dist/kernel/cli-login.js +199 -0
- package/dist/kernel/definition.d.ts +41 -0
- package/dist/kernel/definition.d.ts.map +1 -0
- package/dist/kernel/definition.js +13 -0
- package/dist/kernel/env-template.d.ts +17 -0
- package/dist/kernel/env-template.d.ts.map +1 -0
- package/dist/kernel/env-template.js +78 -0
- package/dist/kernel/facets.d.ts +58 -0
- package/dist/kernel/facets.d.ts.map +1 -0
- package/dist/kernel/facets.js +102 -0
- package/dist/kernel/guard.d.ts +24 -0
- package/dist/kernel/guard.d.ts.map +1 -0
- package/dist/kernel/guard.js +69 -0
- package/dist/kernel/handle.d.ts +29 -0
- package/dist/kernel/handle.d.ts.map +1 -0
- package/dist/kernel/handle.js +97 -0
- package/dist/kernel/keyed.d.ts +19 -0
- package/dist/kernel/keyed.d.ts.map +1 -0
- package/dist/kernel/keyed.js +35 -0
- package/dist/kernel/login.d.ts +20 -0
- package/dist/kernel/login.d.ts.map +1 -0
- package/dist/kernel/login.js +74 -0
- package/dist/kernel/options.d.ts +73 -0
- package/dist/kernel/options.d.ts.map +1 -0
- package/dist/kernel/options.js +41 -0
- package/dist/kernel/run.d.ts +20 -0
- package/dist/kernel/run.d.ts.map +1 -0
- package/dist/kernel/run.js +245 -0
- package/dist/kernel/runtime.d.ts +48 -0
- package/dist/kernel/runtime.d.ts.map +1 -0
- package/dist/kernel/runtime.js +108 -0
- package/dist/keyring/index.d.ts +2 -0
- package/dist/keyring/index.d.ts.map +1 -0
- package/dist/keyring/index.js +1 -0
- package/dist/keyring/resolver.d.ts +9 -0
- package/dist/keyring/resolver.d.ts.map +1 -0
- package/dist/keyring/resolver.js +62 -0
- package/dist/run-agent.d.ts +19 -0
- package/dist/run-agent.d.ts.map +1 -0
- package/dist/run-agent.js +25 -0
- package/dist/structured/extract.d.ts +27 -0
- package/dist/structured/extract.d.ts.map +1 -0
- package/dist/structured/extract.js +188 -0
- package/dist/structured/run-structured.d.ts +83 -0
- package/dist/structured/run-structured.d.ts.map +1 -0
- package/dist/structured/run-structured.js +202 -0
- package/dist/structured/standard-schema.d.ts +34 -0
- package/dist/structured/standard-schema.d.ts.map +1 -0
- package/dist/structured/standard-schema.js +6 -0
- package/dist/testing/fake-agent.d.ts +32 -0
- package/dist/testing/fake-agent.d.ts.map +1 -0
- package/dist/testing/fake-agent.js +43 -0
- package/dist/testing/fake-clock.d.ts +8 -0
- package/dist/testing/fake-clock.d.ts.map +1 -0
- package/dist/testing/fake-clock.js +44 -0
- package/dist/testing/fake-exec.d.ts +26 -0
- package/dist/testing/fake-exec.d.ts.map +1 -0
- package/dist/testing/fake-exec.js +69 -0
- package/dist/testing/fake-secrets.d.ts +3 -0
- package/dist/testing/fake-secrets.d.ts.map +1 -0
- package/dist/testing/fake-secrets.js +5 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +9 -0
- package/package.json +75 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { AsyncQueue } from "../../../core/async-queue.js";
|
|
3
|
+
import { AGENT_EVENT_TYPE, } from "../../../core/run.js";
|
|
4
|
+
import { piSandboxToolPolicy } from "./sandbox.js";
|
|
5
|
+
const PI_PROVIDER_API = "openai-completions";
|
|
6
|
+
const PI_PROVIDER_API_KEY_PLACEHOLDER = "unused";
|
|
7
|
+
const PI_PROVIDER_TEXT_INPUT = "text";
|
|
8
|
+
const PI_PROVIDER_ZERO_COST = 0;
|
|
9
|
+
const PI_HEADER = {
|
|
10
|
+
authorizationLowercase: "authorization",
|
|
11
|
+
authorization: "Authorization",
|
|
12
|
+
};
|
|
13
|
+
const PI_RESOURCE_DIRECTORY = {
|
|
14
|
+
agents: ".agents",
|
|
15
|
+
skills: "skills",
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Session event names, verified against the subscribed union
|
|
19
|
+
* (`@earendil-works/pi-agent-core` 0.75.5, `dist/types.d.ts` `AgentEvent`).
|
|
20
|
+
* Pi emits lifecycle groups — `turn_start`/`turn_end`,
|
|
21
|
+
* `message_start`/`message_update`/`message_end`,
|
|
22
|
+
* `tool_execution_start`/`_update`/`_end` — and the kernel guards count
|
|
23
|
+
* `turn`/`tool` kernel events, so each group must map to exactly ONE
|
|
24
|
+
* kernel event or the guard caps fire early. Every unmapped pi event
|
|
25
|
+
* flows through as `trace`.
|
|
26
|
+
*/
|
|
27
|
+
const PI_SESSION_EVENT_TYPE = {
|
|
28
|
+
turnEnd: "turn_end",
|
|
29
|
+
toolExecutionStart: "tool_execution_start",
|
|
30
|
+
messageEnd: "message_end",
|
|
31
|
+
};
|
|
32
|
+
const PI_EVENT_FIELD = {
|
|
33
|
+
type: "type",
|
|
34
|
+
toolName: "toolName",
|
|
35
|
+
message: "message",
|
|
36
|
+
role: "role",
|
|
37
|
+
content: "content",
|
|
38
|
+
text: "text",
|
|
39
|
+
};
|
|
40
|
+
const PI_CONTENT_BLOCK_TYPE = {
|
|
41
|
+
text: "text",
|
|
42
|
+
};
|
|
43
|
+
const PI_ROLE = {
|
|
44
|
+
assistant: "assistant",
|
|
45
|
+
};
|
|
46
|
+
const EMPTY_TEXT = "";
|
|
47
|
+
const STREAMED_MESSAGE_SEPARATOR = "\n";
|
|
48
|
+
export function runPiSession(input) {
|
|
49
|
+
return new PiSessionRun(input).run();
|
|
50
|
+
}
|
|
51
|
+
class PiSessionRun {
|
|
52
|
+
input;
|
|
53
|
+
queue = new AsyncQueue();
|
|
54
|
+
streamedMessages = [];
|
|
55
|
+
session = null;
|
|
56
|
+
unsubscribe = null;
|
|
57
|
+
promptError = null;
|
|
58
|
+
disposeCalled = false;
|
|
59
|
+
constructor(input) {
|
|
60
|
+
this.input = input;
|
|
61
|
+
}
|
|
62
|
+
async *run() {
|
|
63
|
+
const session = await this.createSession();
|
|
64
|
+
this.session = session;
|
|
65
|
+
const removeAbortListener = installAbortListener(this.input.ctx.signal, () => this.abort());
|
|
66
|
+
try {
|
|
67
|
+
await session.bindExtensions({});
|
|
68
|
+
applyRequestParamsPayloadOverride(session, this.input.config.requestParams);
|
|
69
|
+
this.unsubscribe =
|
|
70
|
+
session.subscribe?.((event) => this.acceptEvent(event)) ?? null;
|
|
71
|
+
const promptDone = this.prompt(session);
|
|
72
|
+
for await (const event of this.queue) {
|
|
73
|
+
yield event;
|
|
74
|
+
}
|
|
75
|
+
if (this.input.ctx.signal.aborted) {
|
|
76
|
+
return this.outcome(this.streamedText());
|
|
77
|
+
}
|
|
78
|
+
await promptDone;
|
|
79
|
+
if (this.promptError !== null) {
|
|
80
|
+
throw this.promptError;
|
|
81
|
+
}
|
|
82
|
+
return this.outcome(this.assistantText(session));
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
removeAbortListener();
|
|
86
|
+
this.unsubscribe?.();
|
|
87
|
+
this.dispose();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async createSession() {
|
|
91
|
+
const authStorage = this.input.module.AuthStorage.inMemory();
|
|
92
|
+
if (this.input.config.apiKey !== null) {
|
|
93
|
+
authStorage.setRuntimeApiKey(this.input.config.id, this.input.config.apiKey);
|
|
94
|
+
}
|
|
95
|
+
const modelRegistry = this.input.module.ModelRegistry.inMemory(authStorage);
|
|
96
|
+
modelRegistry.registerProvider(this.input.config.id, providerConfig(this.input));
|
|
97
|
+
const model = modelRegistry.find(this.input.config.id, this.input.config.model);
|
|
98
|
+
const settingsManager = this.input.module.SettingsManager.inMemory();
|
|
99
|
+
const resourceLoader = new this.input.module.DefaultResourceLoader({
|
|
100
|
+
cwd: this.input.options.workspace,
|
|
101
|
+
agentDir: this.input.agentDir,
|
|
102
|
+
settingsManager,
|
|
103
|
+
additionalSkillPaths: piSkillPaths(this.input.options.workspace, this.input.agentDir),
|
|
104
|
+
});
|
|
105
|
+
await resourceLoader.reload();
|
|
106
|
+
const policy = piSandboxToolPolicy(this.input.options.sandbox, {
|
|
107
|
+
module: this.input.module,
|
|
108
|
+
workspace: this.input.options.workspace,
|
|
109
|
+
});
|
|
110
|
+
const created = await this.input.module.createAgentSession({
|
|
111
|
+
cwd: this.input.options.workspace,
|
|
112
|
+
agentDir: this.input.agentDir,
|
|
113
|
+
authStorage,
|
|
114
|
+
modelRegistry,
|
|
115
|
+
model,
|
|
116
|
+
resourceLoader,
|
|
117
|
+
sessionManager: this.input.module.SessionManager.inMemory(this.input.options.workspace),
|
|
118
|
+
settingsManager,
|
|
119
|
+
...policy,
|
|
120
|
+
});
|
|
121
|
+
return created.session;
|
|
122
|
+
}
|
|
123
|
+
prompt(session) {
|
|
124
|
+
return Promise.resolve()
|
|
125
|
+
.then(() => session.prompt(this.input.options.prompt))
|
|
126
|
+
.then(() => this.queue.end(), (error) => {
|
|
127
|
+
this.promptError = error;
|
|
128
|
+
this.queue.end();
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
acceptEvent(event) {
|
|
132
|
+
const projection = piEventProjection(event);
|
|
133
|
+
if (projection.assistantText !== null) {
|
|
134
|
+
this.streamedMessages.push(projection.assistantText);
|
|
135
|
+
}
|
|
136
|
+
for (const mapped of projection.events) {
|
|
137
|
+
this.queue.push(mapped);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
abort() {
|
|
141
|
+
const session = this.session;
|
|
142
|
+
if (session === null) {
|
|
143
|
+
this.queue.end();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const aborting = session.abort?.();
|
|
147
|
+
if (aborting === undefined) {
|
|
148
|
+
this.queue.end();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
void aborting.then(() => this.queue.end(), () => this.queue.end());
|
|
152
|
+
}
|
|
153
|
+
dispose() {
|
|
154
|
+
if (this.disposeCalled || this.session === null) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.disposeCalled = true;
|
|
158
|
+
this.session.dispose();
|
|
159
|
+
}
|
|
160
|
+
streamedText() {
|
|
161
|
+
return this.streamedMessages.join(STREAMED_MESSAGE_SEPARATOR);
|
|
162
|
+
}
|
|
163
|
+
assistantText(session) {
|
|
164
|
+
return this.streamedMessages.length === 0
|
|
165
|
+
? lastAssistantText(session)
|
|
166
|
+
: this.streamedText();
|
|
167
|
+
}
|
|
168
|
+
outcome(text) {
|
|
169
|
+
return {
|
|
170
|
+
text,
|
|
171
|
+
model: this.input.config.model,
|
|
172
|
+
exit: null,
|
|
173
|
+
raw: null,
|
|
174
|
+
usage: null,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function installAbortListener(signal, abort) {
|
|
179
|
+
if (signal.aborted) {
|
|
180
|
+
abort();
|
|
181
|
+
return () => { };
|
|
182
|
+
}
|
|
183
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
184
|
+
return () => signal.removeEventListener("abort", abort);
|
|
185
|
+
}
|
|
186
|
+
function providerConfig(input) {
|
|
187
|
+
const config = input.config;
|
|
188
|
+
const headers = providerHeaders(config);
|
|
189
|
+
return {
|
|
190
|
+
name: config.id,
|
|
191
|
+
baseUrl: config.baseUrl,
|
|
192
|
+
apiKey: PI_PROVIDER_API_KEY_PLACEHOLDER,
|
|
193
|
+
api: PI_PROVIDER_API,
|
|
194
|
+
authHeader: config.apiKey !== null && !hasHeader(config.headers),
|
|
195
|
+
...(Object.keys(headers).length === 0 ? {} : { headers }),
|
|
196
|
+
models: [
|
|
197
|
+
{
|
|
198
|
+
id: config.model,
|
|
199
|
+
name: config.model,
|
|
200
|
+
api: PI_PROVIDER_API,
|
|
201
|
+
reasoning: false,
|
|
202
|
+
input: [PI_PROVIDER_TEXT_INPUT],
|
|
203
|
+
cost: {
|
|
204
|
+
input: PI_PROVIDER_ZERO_COST,
|
|
205
|
+
output: PI_PROVIDER_ZERO_COST,
|
|
206
|
+
cacheRead: PI_PROVIDER_ZERO_COST,
|
|
207
|
+
cacheWrite: PI_PROVIDER_ZERO_COST,
|
|
208
|
+
},
|
|
209
|
+
contextWindow: input.contextWindow,
|
|
210
|
+
maxTokens: input.maxTokens,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Constraint: pi's openai-completions transport constructs its HTTP client
|
|
217
|
+
* with SOME api key (our placeholder "unused") and that client sends
|
|
218
|
+
* `Authorization: Bearer <apiKey>` unless an explicit Authorization header
|
|
219
|
+
* overrides it. For keyless endpoints with no caller Authorization header
|
|
220
|
+
* we therefore register an explicit EMPTY Authorization header so the
|
|
221
|
+
* placeholder key never reaches the wire.
|
|
222
|
+
*/
|
|
223
|
+
function providerHeaders(config) {
|
|
224
|
+
if (config.apiKey !== null || hasHeader(config.headers)) {
|
|
225
|
+
return { ...config.headers };
|
|
226
|
+
}
|
|
227
|
+
return { ...config.headers, [PI_HEADER.authorization]: EMPTY_TEXT };
|
|
228
|
+
}
|
|
229
|
+
function hasHeader(headers) {
|
|
230
|
+
return Object.keys(headers).some((name) => name.toLowerCase() === PI_HEADER.authorizationLowercase);
|
|
231
|
+
}
|
|
232
|
+
function piSkillPaths(workspace, agentDir) {
|
|
233
|
+
return [
|
|
234
|
+
path.join(workspace, PI_RESOURCE_DIRECTORY.agents, PI_RESOURCE_DIRECTORY.skills),
|
|
235
|
+
path.join(workspace, PI_RESOURCE_DIRECTORY.skills),
|
|
236
|
+
path.join(agentDir, PI_RESOURCE_DIRECTORY.skills),
|
|
237
|
+
];
|
|
238
|
+
}
|
|
239
|
+
function applyRequestParamsPayloadOverride(session, requestParams) {
|
|
240
|
+
if (Object.keys(requestParams).length === 0 || session.agent === undefined) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const previousOnPayload = session.agent.onPayload;
|
|
244
|
+
session.agent.onPayload = async (payload, model) => {
|
|
245
|
+
const previousPayload = await previousOnPayload?.(payload, model);
|
|
246
|
+
const basePayload = previousPayload === undefined ? payload : previousPayload;
|
|
247
|
+
return {
|
|
248
|
+
...(isObjectRecord(basePayload) ? basePayload : {}),
|
|
249
|
+
...requestParams,
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/** One row per mapped pi event type; anything else projects to `trace`. */
|
|
254
|
+
const PI_SESSION_EVENT_PROJECTORS = {
|
|
255
|
+
[PI_SESSION_EVENT_TYPE.turnEnd]: () => ({
|
|
256
|
+
events: [{ type: AGENT_EVENT_TYPE.turn }],
|
|
257
|
+
assistantText: null,
|
|
258
|
+
}),
|
|
259
|
+
[PI_SESSION_EVENT_TYPE.toolExecutionStart]: projectToolExecutionStart,
|
|
260
|
+
[PI_SESSION_EVENT_TYPE.messageEnd]: projectMessageEnd,
|
|
261
|
+
};
|
|
262
|
+
function piEventProjection(event) {
|
|
263
|
+
if (!isObjectRecord(event)) {
|
|
264
|
+
return traceProjection(event);
|
|
265
|
+
}
|
|
266
|
+
const type = readStringField(event, PI_EVENT_FIELD.type);
|
|
267
|
+
if (type === null || !isPiSessionEventType(type)) {
|
|
268
|
+
return traceProjection(event);
|
|
269
|
+
}
|
|
270
|
+
return PI_SESSION_EVENT_PROJECTORS[type](event) ?? traceProjection(event);
|
|
271
|
+
}
|
|
272
|
+
function isPiSessionEventType(type) {
|
|
273
|
+
return Object.values(PI_SESSION_EVENT_TYPE).includes(type);
|
|
274
|
+
}
|
|
275
|
+
function projectToolExecutionStart(event) {
|
|
276
|
+
const toolName = readStringField(event, PI_EVENT_FIELD.toolName);
|
|
277
|
+
return toolName === null
|
|
278
|
+
? null
|
|
279
|
+
: {
|
|
280
|
+
events: [{ type: AGENT_EVENT_TYPE.tool, name: toolName }],
|
|
281
|
+
assistantText: null,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function projectMessageEnd(event) {
|
|
285
|
+
const text = assistantMessageText(event[PI_EVENT_FIELD.message]);
|
|
286
|
+
return text === null
|
|
287
|
+
? null
|
|
288
|
+
: {
|
|
289
|
+
events: [{ type: AGENT_EVENT_TYPE.message, text }],
|
|
290
|
+
assistantText: text,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function traceProjection(data) {
|
|
294
|
+
return {
|
|
295
|
+
events: [{ type: AGENT_EVENT_TYPE.trace, data }],
|
|
296
|
+
assistantText: null,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Extracts the text of a pi `AgentMessage` when it is an assistant
|
|
301
|
+
* message: `{ role: "assistant", content: (TextContent | ...)[] }` with
|
|
302
|
+
* text blocks `{ type: "text", text }` (pi-ai 0.75.5). A plain string
|
|
303
|
+
* `content` is accepted as already-joined text. `null` = not an assistant
|
|
304
|
+
* text message.
|
|
305
|
+
*/
|
|
306
|
+
function assistantMessageText(message) {
|
|
307
|
+
if (!isObjectRecord(message)) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
if (readStringField(message, PI_EVENT_FIELD.role) !== PI_ROLE.assistant) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
const content = message[PI_EVENT_FIELD.content];
|
|
314
|
+
if (typeof content === "string") {
|
|
315
|
+
return content === EMPTY_TEXT ? null : content;
|
|
316
|
+
}
|
|
317
|
+
if (!Array.isArray(content)) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
const text = content
|
|
321
|
+
.map((block) => textBlockText(block))
|
|
322
|
+
.filter((blockText) => blockText !== null)
|
|
323
|
+
.join(EMPTY_TEXT);
|
|
324
|
+
return text === EMPTY_TEXT ? null : text;
|
|
325
|
+
}
|
|
326
|
+
function textBlockText(block) {
|
|
327
|
+
if (!isObjectRecord(block)) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
if (readStringField(block, PI_EVENT_FIELD.type) !== PI_CONTENT_BLOCK_TYPE.text) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return readStringField(block, PI_EVENT_FIELD.text);
|
|
334
|
+
}
|
|
335
|
+
function lastAssistantText(session) {
|
|
336
|
+
const messages = session.state?.messages;
|
|
337
|
+
if (!Array.isArray(messages)) {
|
|
338
|
+
return EMPTY_TEXT;
|
|
339
|
+
}
|
|
340
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
341
|
+
const text = assistantMessageText(messages[index]);
|
|
342
|
+
if (text !== null) {
|
|
343
|
+
return text;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return EMPTY_TEXT;
|
|
347
|
+
}
|
|
348
|
+
function readStringField(record, key) {
|
|
349
|
+
const value = record[key];
|
|
350
|
+
return typeof value === "string" ? value : null;
|
|
351
|
+
}
|
|
352
|
+
function isObjectRecord(value) {
|
|
353
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
354
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AgentRunOutcome } from "../../core/run.js";
|
|
2
|
+
import { type AgentEvent } from "../../core/run.js";
|
|
3
|
+
export interface OpenAiCompatibleResponseDecoder {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
supports(response: Response): boolean;
|
|
6
|
+
decode(response: Response, model: string): AsyncGenerator<AgentEvent, AgentRunOutcome, void>;
|
|
7
|
+
}
|
|
8
|
+
export declare class EventStreamOpenAiCompatibleResponseDecoder implements OpenAiCompatibleResponseDecoder {
|
|
9
|
+
readonly id: "event-stream";
|
|
10
|
+
supports(response: Response): boolean;
|
|
11
|
+
decode(response: Response, model: string): AsyncGenerator<AgentEvent, AgentRunOutcome, void>;
|
|
12
|
+
}
|
|
13
|
+
export declare class JsonOpenAiCompatibleResponseDecoder implements OpenAiCompatibleResponseDecoder {
|
|
14
|
+
readonly id: "json";
|
|
15
|
+
supports(response: Response): boolean;
|
|
16
|
+
decode(response: Response, model: string): AsyncGenerator<AgentEvent, AgentRunOutcome, void>;
|
|
17
|
+
}
|
|
18
|
+
export declare function createDefaultOpenAiCompatibleResponseDecoders(): readonly OpenAiCompatibleResponseDecoder[];
|
|
19
|
+
//# sourceMappingURL=response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../../src/agents/openai-compatible/response.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAY,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA0BtE,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC;IACtC,MAAM,CACJ,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GACZ,cAAc,CAAC,UAAU,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC;CACtD;AAED,qBAAa,0CACX,YAAW,+BAA+B;IAE1C,QAAQ,CAAC,EAAE,iBAA0B;IAErC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO;IAO9B,MAAM,CACX,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GACZ,cAAc,CAAC,UAAU,EAAE,eAAe,EAAE,IAAI,CAAC;CAwBrD;AAED,qBAAa,mCACX,YAAW,+BAA+B;IAE1C,QAAQ,CAAC,EAAE,SAAmB;IAE9B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO;IAU9B,MAAM,CACX,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GACZ,cAAc,CAAC,UAAU,EAAE,eAAe,EAAE,IAAI,CAAC;CAmBrD;AAED,wBAAgB,6CAA6C,IAAI,SAAS,+BAA+B,EAAE,CAK1G"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { AGENT_ERROR_CODE, AgentError } from "../../core/error.js";
|
|
2
|
+
import { redactSecrets } from "../../core/redact.js";
|
|
3
|
+
import { AGENT_EVENT_TYPE } from "../../core/run.js";
|
|
4
|
+
const CONTENT_TYPE_HEADER = "content-type";
|
|
5
|
+
const APPLICATION_JSON = "application/json";
|
|
6
|
+
const JSON_CONTENT_TYPE_SUFFIX = "+json";
|
|
7
|
+
const TEXT_EVENT_STREAM = "text/event-stream";
|
|
8
|
+
const STREAM_DONE = "[DONE]";
|
|
9
|
+
const STDERR_TAIL_CHARS = 2_000;
|
|
10
|
+
const DECODER_ID = {
|
|
11
|
+
eventStream: "event-stream",
|
|
12
|
+
json: "json",
|
|
13
|
+
};
|
|
14
|
+
const SSE_DATA_PREFIX = "data:";
|
|
15
|
+
const OPENAI_RESPONSE_FIELD = {
|
|
16
|
+
choices: "choices",
|
|
17
|
+
completionTokens: "completion_tokens",
|
|
18
|
+
content: "content",
|
|
19
|
+
delta: "delta",
|
|
20
|
+
message: "message",
|
|
21
|
+
promptTokens: "prompt_tokens",
|
|
22
|
+
text: "text",
|
|
23
|
+
usage: "usage",
|
|
24
|
+
};
|
|
25
|
+
export class EventStreamOpenAiCompatibleResponseDecoder {
|
|
26
|
+
id = DECODER_ID.eventStream;
|
|
27
|
+
supports(response) {
|
|
28
|
+
return (response.headers.get(CONTENT_TYPE_HEADER)?.includes(TEXT_EVENT_STREAM) ===
|
|
29
|
+
true);
|
|
30
|
+
}
|
|
31
|
+
async *decode(response, model) {
|
|
32
|
+
const reader = response.body?.getReader();
|
|
33
|
+
if (reader === undefined) {
|
|
34
|
+
return emptyOutcome(model);
|
|
35
|
+
}
|
|
36
|
+
const decoder = new TextDecoder();
|
|
37
|
+
const state = {
|
|
38
|
+
buffer: "",
|
|
39
|
+
text: "",
|
|
40
|
+
usage: null,
|
|
41
|
+
};
|
|
42
|
+
for (;;) {
|
|
43
|
+
const { done, value } = await reader.read();
|
|
44
|
+
if (done) {
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
state.buffer += decoder.decode(value, { stream: true });
|
|
48
|
+
yield* drainEventStreamLines(state);
|
|
49
|
+
}
|
|
50
|
+
state.buffer += decoder.decode();
|
|
51
|
+
yield* drainFinalEventStreamLine(state);
|
|
52
|
+
return { ...emptyOutcome(model), text: state.text, usage: state.usage };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class JsonOpenAiCompatibleResponseDecoder {
|
|
56
|
+
id = DECODER_ID.json;
|
|
57
|
+
supports(response) {
|
|
58
|
+
const contentType = response.headers
|
|
59
|
+
.get(CONTENT_TYPE_HEADER)
|
|
60
|
+
?.toLowerCase();
|
|
61
|
+
return (contentType?.includes(APPLICATION_JSON) === true ||
|
|
62
|
+
contentType?.includes(JSON_CONTENT_TYPE_SUFFIX) === true);
|
|
63
|
+
}
|
|
64
|
+
async *decode(response, model) {
|
|
65
|
+
const body = await response.text();
|
|
66
|
+
const parsed = parseJsonResponse(body);
|
|
67
|
+
const text = extractCompletionText(parsed);
|
|
68
|
+
if (text === null) {
|
|
69
|
+
throw invalidJsonResponseError("OpenAI-compatible endpoint returned JSON without completion text", body);
|
|
70
|
+
}
|
|
71
|
+
if (text !== "") {
|
|
72
|
+
yield { type: AGENT_EVENT_TYPE.message, text };
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
...emptyOutcome(model),
|
|
76
|
+
text,
|
|
77
|
+
usage: extractUsage(parsed),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function createDefaultOpenAiCompatibleResponseDecoders() {
|
|
82
|
+
return [
|
|
83
|
+
new EventStreamOpenAiCompatibleResponseDecoder(),
|
|
84
|
+
new JsonOpenAiCompatibleResponseDecoder(),
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
function* drainEventStreamLines(state) {
|
|
88
|
+
const lines = state.buffer.split(/\r?\n/u);
|
|
89
|
+
state.buffer = lines.pop() ?? "";
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
const event = handleEventStreamLine(state, line);
|
|
92
|
+
if (event !== null) {
|
|
93
|
+
yield event;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function* drainFinalEventStreamLine(state) {
|
|
98
|
+
if (state.buffer.trim() === "") {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const event = handleEventStreamLine(state, state.buffer);
|
|
102
|
+
if (event !== null) {
|
|
103
|
+
yield event;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function handleEventStreamLine(state, line) {
|
|
107
|
+
const trimmed = line.trim();
|
|
108
|
+
if (!trimmed.startsWith(SSE_DATA_PREFIX)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const data = trimmed.slice(SSE_DATA_PREFIX.length).trim();
|
|
112
|
+
if (data === "" || data === STREAM_DONE) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
const parsed = safeJson(data);
|
|
116
|
+
const chunk = extractCompletionText(parsed);
|
|
117
|
+
state.usage = extractUsage(parsed) ?? state.usage;
|
|
118
|
+
if (chunk === null || chunk === "") {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
state.text += chunk;
|
|
122
|
+
return { type: AGENT_EVENT_TYPE.message, text: chunk };
|
|
123
|
+
}
|
|
124
|
+
function emptyOutcome(model) {
|
|
125
|
+
return {
|
|
126
|
+
text: "",
|
|
127
|
+
model,
|
|
128
|
+
exit: null,
|
|
129
|
+
raw: null,
|
|
130
|
+
usage: null,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function extractCompletionText(value) {
|
|
134
|
+
const firstChoice = readArray(readRecord(value), OPENAI_RESPONSE_FIELD.choices)[0];
|
|
135
|
+
const choice = readRecord(firstChoice);
|
|
136
|
+
const deltaText = readString(readRecord(choice?.[OPENAI_RESPONSE_FIELD.delta]), OPENAI_RESPONSE_FIELD.content);
|
|
137
|
+
if (deltaText !== null) {
|
|
138
|
+
return deltaText;
|
|
139
|
+
}
|
|
140
|
+
const messageText = readString(readRecord(choice?.[OPENAI_RESPONSE_FIELD.message]), OPENAI_RESPONSE_FIELD.content);
|
|
141
|
+
if (messageText !== null) {
|
|
142
|
+
return messageText;
|
|
143
|
+
}
|
|
144
|
+
return readString(choice, OPENAI_RESPONSE_FIELD.text);
|
|
145
|
+
}
|
|
146
|
+
function extractUsage(value) {
|
|
147
|
+
const usage = readRecord(readRecord(value)?.[OPENAI_RESPONSE_FIELD.usage]);
|
|
148
|
+
if (usage === null) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
inputTokens: readNumber(usage, OPENAI_RESPONSE_FIELD.promptTokens),
|
|
153
|
+
outputTokens: readNumber(usage, OPENAI_RESPONSE_FIELD.completionTokens),
|
|
154
|
+
costUsd: null,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function readRecord(value) {
|
|
158
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
159
|
+
? value
|
|
160
|
+
: null;
|
|
161
|
+
}
|
|
162
|
+
function readArray(record, key) {
|
|
163
|
+
const value = record?.[key];
|
|
164
|
+
return Array.isArray(value) ? value : [];
|
|
165
|
+
}
|
|
166
|
+
function readString(record, key) {
|
|
167
|
+
const value = record?.[key];
|
|
168
|
+
return typeof value === "string" ? value : null;
|
|
169
|
+
}
|
|
170
|
+
function readNumber(record, key) {
|
|
171
|
+
const value = record[key];
|
|
172
|
+
return typeof value === "number" ? value : null;
|
|
173
|
+
}
|
|
174
|
+
function safeJson(text) {
|
|
175
|
+
try {
|
|
176
|
+
return JSON.parse(text);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function parseJsonResponse(body) {
|
|
183
|
+
try {
|
|
184
|
+
return JSON.parse(body);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
throw invalidJsonResponseError("OpenAI-compatible endpoint returned malformed JSON", body);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function invalidJsonResponseError(message, body) {
|
|
191
|
+
const redactedBody = redactSecrets(body);
|
|
192
|
+
return new AgentError({
|
|
193
|
+
code: AGENT_ERROR_CODE.failed,
|
|
194
|
+
message,
|
|
195
|
+
data: {
|
|
196
|
+
exitCode: null,
|
|
197
|
+
stdoutTail: null,
|
|
198
|
+
stderrTail: redactedBody.length === 0
|
|
199
|
+
? null
|
|
200
|
+
: redactedBody.slice(-STDERR_TAIL_CHARS),
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { JsonValue } from "@alpacakit/core";
|
|
2
|
+
import type { EnvTemplate } from "../../kernel/env-template.js";
|
|
3
|
+
export type { JsonValue } from "@alpacakit/core";
|
|
4
|
+
export type OpenAiCompatibleConfig<Id extends string = string> = {
|
|
5
|
+
readonly id: Id;
|
|
6
|
+
readonly baseUrl: EnvTemplate;
|
|
7
|
+
readonly model: EnvTemplate;
|
|
8
|
+
readonly apiKey: EnvTemplate | null;
|
|
9
|
+
readonly headers: Readonly<Record<string, EnvTemplate>>;
|
|
10
|
+
readonly requestParams: Readonly<Record<string, JsonValue>>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Inbound config for `openaiCompatible` (rule 1): the fields that default
|
|
14
|
+
* cleanly to "absent" are optional — `apiKey` to `null`, `headers` and
|
|
15
|
+
* `requestParams` to `{}`. The factory normalizes this into the full
|
|
16
|
+
* `OpenAiCompatibleConfig` exactly once.
|
|
17
|
+
*/
|
|
18
|
+
export type OpenAiCompatibleConfigInput<Id extends string = string> = {
|
|
19
|
+
readonly id: Id;
|
|
20
|
+
readonly baseUrl: EnvTemplate;
|
|
21
|
+
readonly model: EnvTemplate;
|
|
22
|
+
readonly apiKey?: EnvTemplate | null;
|
|
23
|
+
readonly headers?: Readonly<Record<string, EnvTemplate>>;
|
|
24
|
+
readonly requestParams?: Readonly<Record<string, JsonValue>>;
|
|
25
|
+
};
|
|
26
|
+
export type ResolvedOpenAiCompatibleConfig = {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly baseUrl: string;
|
|
29
|
+
readonly model: string;
|
|
30
|
+
readonly apiKey: string | null;
|
|
31
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
32
|
+
readonly requestParams: Readonly<Record<string, JsonValue>>;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/agents/openai-compatible/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,sBAAsB,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI;IAC/D,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;CAC7D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI;IACpE,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;CAC9D,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;CAC7D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AgentDefinition } from "../kernel/definition.js";
|
|
2
|
+
import { type OpenAiCompatibleConfigResolver } from "./openai-compatible/config.js";
|
|
3
|
+
import type { OpenAiCompatibleEngine } from "./openai-compatible/engine.js";
|
|
4
|
+
import type { OpenAiCompatibleConfigInput } from "./openai-compatible/types.js";
|
|
5
|
+
export type { OpenAiCompatibleEngine } from "./openai-compatible/engine.js";
|
|
6
|
+
export type { JsonValue, OpenAiCompatibleConfig, OpenAiCompatibleConfigInput, } from "./openai-compatible/types.js";
|
|
7
|
+
type OpenAiCompatibleDependencies = {
|
|
8
|
+
readonly configResolver?: OpenAiCompatibleConfigResolver;
|
|
9
|
+
readonly engine?: OpenAiCompatibleEngine;
|
|
10
|
+
};
|
|
11
|
+
export declare function openaiCompatible<const Id extends string>(config: OpenAiCompatibleConfigInput<Id>, dependencies?: OpenAiCompatibleDependencies): AgentDefinition<Id>;
|
|
12
|
+
//# sourceMappingURL=openai-compatible.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-compatible.d.ts","sourceRoot":"","sources":["../../src/agents/openai-compatible.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,eAAe,EAGrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAE5E,OAAO,KAAK,EAEV,2BAA2B,EAC5B,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,YAAY,EACV,SAAS,EACT,sBAAsB,EACtB,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC;AAEtC,KAAK,4BAA4B,GAAG;IAClC,QAAQ,CAAC,cAAc,CAAC,EAAE,8BAA8B,CAAC;IACzD,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAC;CAC1C,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EACtD,MAAM,EAAE,2BAA2B,CAAC,EAAE,CAAC,EACvC,YAAY,GAAE,4BAAiC,GAC9C,eAAe,CAAC,EAAE,CAAC,CA8BrB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DOCTOR_CHECK_STATUS } from "../host/capabilities.js";
|
|
2
|
+
import { defineAgent, } from "../kernel/definition.js";
|
|
3
|
+
import { defaultOpenAiCompatibleConfigResolver, } from "./openai-compatible/config.js";
|
|
4
|
+
import { defaultOpenAiCompatibleEngine } from "./openai-compatible/fetch-engine.js";
|
|
5
|
+
export function openaiCompatible(config, dependencies = {}) {
|
|
6
|
+
const configResolver = dependencies.configResolver ?? defaultOpenAiCompatibleConfigResolver;
|
|
7
|
+
const engine = dependencies.engine ?? defaultOpenAiCompatibleEngine;
|
|
8
|
+
const normalized = configResolver.normalize(withConfigDefaults(config));
|
|
9
|
+
return defineAgent({
|
|
10
|
+
id: normalized.id,
|
|
11
|
+
run: (options, ctx) => runOpenAiCompatible({
|
|
12
|
+
config: normalized,
|
|
13
|
+
configResolver,
|
|
14
|
+
engine,
|
|
15
|
+
options,
|
|
16
|
+
ctx,
|
|
17
|
+
}),
|
|
18
|
+
doctor: {
|
|
19
|
+
probe: async (ctx) => {
|
|
20
|
+
const checks = [
|
|
21
|
+
...configResolver.doctorChecks(normalized, ctx.env),
|
|
22
|
+
...(await engine.doctorChecks()),
|
|
23
|
+
];
|
|
24
|
+
return {
|
|
25
|
+
available: checks.every((check) => check.status === DOCTOR_CHECK_STATUS.pass),
|
|
26
|
+
checks,
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function withConfigDefaults(config) {
|
|
33
|
+
return {
|
|
34
|
+
id: config.id,
|
|
35
|
+
baseUrl: config.baseUrl,
|
|
36
|
+
model: config.model,
|
|
37
|
+
apiKey: config.apiKey ?? null,
|
|
38
|
+
headers: config.headers ?? {},
|
|
39
|
+
requestParams: config.requestParams ?? {},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async function* runOpenAiCompatible(input) {
|
|
43
|
+
const resolved = input.configResolver.resolve(input.config, input.ctx.env);
|
|
44
|
+
return yield* input.engine.run(resolved, input.options, input.ctx);
|
|
45
|
+
}
|