@apolloyh/apollo-agent 0.1.10 → 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/.env.example +7 -0
- package/README.md +1 -0
- package/dist/brand.d.ts +1 -1
- package/dist/brand.js +1 -1
- package/dist/config.d.ts +4 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -15
- package/dist/index.js +55 -23
- package/dist/llm/anthropic.d.ts +21 -10
- package/dist/llm/anthropic.d.ts.map +1 -1
- package/dist/llm/anthropic.js +38 -10
- package/dist/llm/openai.d.ts +8 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +214 -0
- package/dist/runtime/query-engine.d.ts +8 -0
- package/dist/runtime/query-engine.d.ts.map +1 -1
- package/dist/runtime/query-engine.js +26 -6
- package/dist/sdk.d.ts +2 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +2 -1
- package/dist/thought-fold.d.ts +6 -21
- package/dist/thought-fold.d.ts.map +1 -1
- package/dist/thought-fold.js +23 -98
- package/dist/trace.d.ts.map +1 -1
- package/dist/trace.js +19 -5
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/guide.md +13 -2
- package/docs/npm-release.md +251 -0
- package/docs/sdk.md +4 -0
- package/package.json +3 -2
package/.env.example
CHANGED
|
@@ -16,3 +16,10 @@ ANTHROPIC_DEFAULT_SONNET_MODEL_NAME=glm-5.2
|
|
|
16
16
|
# ANTHROPIC_TIMEOUT_MS=300000
|
|
17
17
|
# ANTHROPIC_CACHE_TTL=5m
|
|
18
18
|
# AGENT_SENDMAIL_PATH=/usr/sbin/sendmail
|
|
19
|
+
|
|
20
|
+
# OpenAI-compatible alternative
|
|
21
|
+
# APOLLO_LLM_PROTOCOL=openai
|
|
22
|
+
# OPENAI_API_KEY=
|
|
23
|
+
# OPENAI_BASE_URL=https://api.openai.com/v1
|
|
24
|
+
# OPENAI_MODEL=gpt-4o-mini
|
|
25
|
+
# OPENAI_MAX_TOKENS_PARAM=max_tokens
|
package/README.md
CHANGED
package/dist/brand.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Product identity — single source of truth for display strings. */
|
|
2
2
|
export declare const PRODUCT: {
|
|
3
3
|
readonly name: "Apollo";
|
|
4
|
-
readonly version: "0.
|
|
4
|
+
readonly version: "0.2.0";
|
|
5
5
|
readonly tagline: "Your personal agent — answer first, act when needed.";
|
|
6
6
|
readonly packageName: "@apolloyh/apollo-agent";
|
|
7
7
|
readonly userAgent: "ApolloAgent/0.1";
|
package/dist/brand.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Product identity — single source of truth for display strings. */
|
|
2
2
|
export const PRODUCT = {
|
|
3
3
|
name: "Apollo",
|
|
4
|
-
version: "0.
|
|
4
|
+
version: "0.2.0",
|
|
5
5
|
tagline: "Your personal agent — answer first, act when needed.",
|
|
6
6
|
packageName: "@apolloyh/apollo-agent",
|
|
7
7
|
userAgent: "ApolloAgent/0.1",
|
package/dist/config.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentConfig, LlmConfig, SubagentConfig } from "./types.js";
|
|
2
2
|
/** Default Apollo Task roles. */
|
|
3
3
|
export declare const DEFAULT_AGENTS: SubagentConfig[];
|
|
4
|
-
export declare const MISSING_AUTH_TOKEN_MESSAGE = "Missing
|
|
4
|
+
export declare const MISSING_AUTH_TOKEN_MESSAGE = "Missing model API token. Set ANTHROPIC_AUTH_TOKEN or OPENAI_API_KEY, then restart Apollo.";
|
|
5
5
|
export declare function ensureUserEnvFile(): {
|
|
6
6
|
path: string;
|
|
7
7
|
created: boolean;
|
|
@@ -14,7 +14,9 @@ export declare function ensureUserConfigExampleFile(): {
|
|
|
14
14
|
path: string;
|
|
15
15
|
created: boolean;
|
|
16
16
|
};
|
|
17
|
-
export declare function loadAgentConfig(configPath?: string
|
|
17
|
+
export declare function loadAgentConfig(configPath?: string, options?: {
|
|
18
|
+
isolated?: boolean;
|
|
19
|
+
}): Promise<AgentConfig>;
|
|
18
20
|
/**
|
|
19
21
|
* Load an explicit env file, or discover project env then ~/.apollo/.env.
|
|
20
22
|
* By default **overrides** existing shell vars so local agent config wins.
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzE,iCAAiC;AACjC,eAAO,MAAM,cAAc,EAAE,cAAc,EAkC1C,CAAC;AAsBF,eAAO,MAAM,0BAA0B,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzE,iCAAiC;AACjC,eAAO,MAAM,cAAc,EAAE,cAAc,EAkC1C,CAAC;AAsBF,eAAO,MAAM,0BAA0B,8FACsD,CAAC;AA8B9F,wBAAgB,iBAAiB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAatE;AAED,wBAAgB,oBAAoB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAazE;AAED,wBAAgB,2BAA2B,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAchF;AAED,wBAAsB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAkDrH;AAiBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAwB3F;AAYD,wBAAgB,aAAa,CAAC,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,SAAS,CAsCrF"}
|
package/dist/config.js
CHANGED
|
@@ -55,7 +55,7 @@ const defaultConfig = {
|
|
|
55
55
|
};
|
|
56
56
|
const DEFAULT_CONFIG_PATH = ".apollo/config.json";
|
|
57
57
|
const LEGACY_CONFIG_PATH = "agent.config.json";
|
|
58
|
-
export const MISSING_AUTH_TOKEN_MESSAGE = "Missing
|
|
58
|
+
export const MISSING_AUTH_TOKEN_MESSAGE = "Missing model API token. Set ANTHROPIC_AUTH_TOKEN or OPENAI_API_KEY, then restart Apollo.";
|
|
59
59
|
const USER_ENV_TEMPLATE = `# Apollo uses Anthropic-compatible environment variable names.
|
|
60
60
|
# Add your provider token below, then restart Apollo.
|
|
61
61
|
ANTHROPIC_AUTH_TOKEN=
|
|
@@ -63,6 +63,12 @@ ANTHROPIC_AUTH_TOKEN=
|
|
|
63
63
|
# Optional provider settings:
|
|
64
64
|
# ANTHROPIC_BASE_URL=https://api.anthropic.com
|
|
65
65
|
# ANTHROPIC_MODEL=claude-haiku-4-5
|
|
66
|
+
|
|
67
|
+
# OpenAI-compatible alternative:
|
|
68
|
+
# APOLLO_LLM_PROTOCOL=openai
|
|
69
|
+
# OPENAI_API_KEY=
|
|
70
|
+
# OPENAI_BASE_URL=https://api.openai.com/v1
|
|
71
|
+
# OPENAI_MODEL=gpt-4o-mini
|
|
66
72
|
`;
|
|
67
73
|
const USER_CONFIG_TEMPLATE = `${JSON.stringify({
|
|
68
74
|
systemPrompt: "",
|
|
@@ -122,12 +128,12 @@ export function ensureUserConfigExampleFile() {
|
|
|
122
128
|
throw error;
|
|
123
129
|
}
|
|
124
130
|
}
|
|
125
|
-
export async function loadAgentConfig(configPath) {
|
|
131
|
+
export async function loadAgentConfig(configPath, options = {}) {
|
|
126
132
|
const userConfigPath = path.join(os.homedir(), ".apollo", "config.json");
|
|
127
133
|
const projectConfigPath = [DEFAULT_CONFIG_PATH, LEGACY_CONFIG_PATH].find(fsSync.existsSync);
|
|
128
134
|
const layers = [
|
|
129
|
-
...(fsSync.existsSync(userConfigPath) ? [{ file: userConfigPath, projectScoped: false }] : []),
|
|
130
|
-
...(projectConfigPath ? [{ file: projectConfigPath, projectScoped: true }] : []),
|
|
135
|
+
...(!options.isolated && fsSync.existsSync(userConfigPath) ? [{ file: userConfigPath, projectScoped: false }] : []),
|
|
136
|
+
...(!options.isolated && projectConfigPath ? [{ file: projectConfigPath, projectScoped: true }] : []),
|
|
131
137
|
...(configPath ? [{ file: configPath, projectScoped: true }] : []),
|
|
132
138
|
];
|
|
133
139
|
const seen = new Set();
|
|
@@ -223,13 +229,15 @@ function unwrapEnvValue(value) {
|
|
|
223
229
|
return value;
|
|
224
230
|
}
|
|
225
231
|
export function loadLlmConfig(options = {}) {
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
const requestedProtocol = process.env.APOLLO_LLM_PROTOCOL ?? (process.env.OPENAI_API_KEY && !process.env.ANTHROPIC_AUTH_TOKEN ? "openai" : "anthropic");
|
|
233
|
+
if (requestedProtocol !== "anthropic" && requestedProtocol !== "openai")
|
|
234
|
+
throw new Error("APOLLO_LLM_PROTOCOL must be anthropic or openai.");
|
|
235
|
+
const protocol = requestedProtocol;
|
|
236
|
+
const authToken = protocol === "openai" ? process.env.OPENAI_API_KEY : process.env.ANTHROPIC_AUTH_TOKEN;
|
|
237
|
+
const baseUrl = protocol === "openai" ? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1" : process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com";
|
|
238
|
+
const model = protocol === "openai"
|
|
239
|
+
? process.env.OPENAI_MODEL ?? "gpt-4o-mini"
|
|
240
|
+
: process.env.ANTHROPIC_MODEL || process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL || process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME || process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME || "claude-haiku-4-5";
|
|
233
241
|
if (!authToken && !options.allowMissingAuth)
|
|
234
242
|
throw new Error(MISSING_AUTH_TOKEN_MESSAGE);
|
|
235
243
|
const cacheTtl = process.env.ANTHROPIC_CACHE_TTL ?? "5m";
|
|
@@ -237,25 +245,30 @@ export function loadLlmConfig(options = {}) {
|
|
|
237
245
|
throw new Error("ANTHROPIC_CACHE_TTL must be 5m or 1h.");
|
|
238
246
|
}
|
|
239
247
|
const providerDefaultMaxTokens = /(?:^glm-|bigmodel\.cn)/i.test(`${model} ${baseUrl}`) ? 128 * 1024 : 4096;
|
|
240
|
-
const maxTokens = positiveNumberEnv("ANTHROPIC_MAX_TOKENS", providerDefaultMaxTokens, true);
|
|
241
|
-
const timeoutMs = positiveNumberEnv("ANTHROPIC_TIMEOUT_MS", 300000, true);
|
|
248
|
+
const maxTokens = positiveNumberEnv(protocol === "openai" ? "OPENAI_MAX_TOKENS" : "ANTHROPIC_MAX_TOKENS", providerDefaultMaxTokens, true);
|
|
249
|
+
const timeoutMs = positiveNumberEnv(protocol === "openai" ? "OPENAI_TIMEOUT_MS" : "ANTHROPIC_TIMEOUT_MS", 300000, true);
|
|
250
|
+
const openaiMaxTokensParam = process.env.OPENAI_MAX_TOKENS_PARAM ?? (/^(?:o[134]-|gpt-5)/i.test(model) ? "max_completion_tokens" : "max_tokens");
|
|
251
|
+
if (openaiMaxTokensParam !== "max_tokens" && openaiMaxTokensParam !== "max_completion_tokens")
|
|
252
|
+
throw new Error("OPENAI_MAX_TOKENS_PARAM must be max_tokens or max_completion_tokens.");
|
|
242
253
|
let parsedBaseUrl;
|
|
243
254
|
try {
|
|
244
255
|
parsedBaseUrl = new URL(baseUrl);
|
|
245
256
|
}
|
|
246
257
|
catch {
|
|
247
|
-
throw new Error("
|
|
258
|
+
throw new Error("Model base URL must be a valid HTTP or HTTPS URL.");
|
|
248
259
|
}
|
|
249
260
|
if (parsedBaseUrl.protocol !== "http:" && parsedBaseUrl.protocol !== "https:") {
|
|
250
|
-
throw new Error("
|
|
261
|
+
throw new Error("Model base URL must be a valid HTTP or HTTPS URL.");
|
|
251
262
|
}
|
|
252
263
|
return {
|
|
264
|
+
protocol,
|
|
253
265
|
authToken: authToken ?? "",
|
|
254
266
|
baseUrl,
|
|
255
267
|
model,
|
|
256
268
|
maxTokens,
|
|
257
269
|
timeoutMs,
|
|
258
270
|
cacheTtl,
|
|
271
|
+
openaiMaxTokensParam,
|
|
259
272
|
};
|
|
260
273
|
}
|
|
261
274
|
function positiveNumberEnv(name, fallback, integer) {
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ let liveStatus;
|
|
|
21
21
|
let runningTaskCount = 0;
|
|
22
22
|
let activeVerb = "Thinking";
|
|
23
23
|
let assistantResponseStarted = false;
|
|
24
|
+
let liveThinkingPreview = "";
|
|
24
25
|
/** Prevent double green/red outcome lines in one user turn */
|
|
25
26
|
let outcomePrintedThisTurn = false;
|
|
26
27
|
let pastedTextSequence = 0;
|
|
@@ -97,31 +98,19 @@ async function main() {
|
|
|
97
98
|
// never reaches the interactive `runtime` binding). Also lets SIGINT close whichever is active.
|
|
98
99
|
let activeRuntime;
|
|
99
100
|
const traceSink = (event) => {
|
|
100
|
-
|
|
101
|
+
let completedThought = null;
|
|
102
|
+
// Collect only model thinking tokens into the collapsible Thought.
|
|
101
103
|
if (event.type === "llm_request") {
|
|
102
104
|
thoughtFold.beginTurn();
|
|
103
|
-
thoughtFold.note(`turn ${event.turn} · ${event.toolCount} tools available`);
|
|
104
105
|
}
|
|
105
|
-
else if (event.type === "
|
|
106
|
-
thoughtFold.
|
|
107
|
-
}
|
|
108
|
-
else if (event.type === "tool_call") {
|
|
109
|
-
thoughtFold.noteTool(event.tool);
|
|
110
|
-
}
|
|
111
|
-
else if (event.type === "tool_result") {
|
|
112
|
-
thoughtFold.note(`${event.tool} ${event.isError ? "failed" : "ok"}`);
|
|
113
|
-
}
|
|
114
|
-
else if (event.type === "task_start") {
|
|
115
|
-
thoughtFold.note(`Task(${event.subagentType}): ${event.description}`);
|
|
116
|
-
}
|
|
117
|
-
else if (event.type === "task_finish") {
|
|
118
|
-
thoughtFold.note(`Task(${event.subagentType}) finished`);
|
|
106
|
+
else if (event.type === "thinking_delta") {
|
|
107
|
+
thoughtFold.appendThinking(event.text);
|
|
119
108
|
}
|
|
120
109
|
else if (event.type === "assistant_delta") {
|
|
121
|
-
thoughtFold.finalizeAndPrint();
|
|
110
|
+
completedThought = thoughtFold.finalizeAndPrint();
|
|
122
111
|
}
|
|
123
112
|
else if (event.type === "llm_response" && event.stopReason !== "tool_use") {
|
|
124
|
-
thoughtFold.finalizeAndPrint();
|
|
113
|
+
completedThought = thoughtFold.finalizeAndPrint();
|
|
125
114
|
}
|
|
126
115
|
if (event.type === "workflow_status") {
|
|
127
116
|
const bar = workflowStatusBar({
|
|
@@ -166,6 +155,8 @@ async function main() {
|
|
|
166
155
|
if (renderBeforeStatus)
|
|
167
156
|
rendererSink(event);
|
|
168
157
|
updateLiveStatus(event, interactiveMode);
|
|
158
|
+
if (interactiveMode && completedThought)
|
|
159
|
+
thoughtFold.printPreview(completedThought);
|
|
169
160
|
taskActivity.record(event);
|
|
170
161
|
if (!renderBeforeStatus)
|
|
171
162
|
rendererSink(event);
|
|
@@ -513,10 +504,6 @@ async function main() {
|
|
|
513
504
|
stdout.write(notes.map((n) => `- ${n.id} ${n.title}\n ${n.content.slice(0, 120).replace(/\n/g, " ")}`).join("\n") + "\n");
|
|
514
505
|
continue;
|
|
515
506
|
}
|
|
516
|
-
if (text === "/thought" || text === "/think" || text === "/t") {
|
|
517
|
-
thoughtFold.toggle();
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
507
|
if (text.startsWith("/verbose")) {
|
|
521
508
|
const value = text.split(/\s+/)[1];
|
|
522
509
|
traceOptions.verbose = value === "on" ? true : value === "off" ? false : !traceOptions.verbose;
|
|
@@ -756,8 +743,19 @@ function updateLiveStatus(event, enabled) {
|
|
|
756
743
|
switch (event.type) {
|
|
757
744
|
case "llm_request":
|
|
758
745
|
assistantResponseStarted = false;
|
|
746
|
+
liveThinkingPreview = "";
|
|
759
747
|
startLiveStatus(runningTaskCount > 0 ? formatRunningTasks() : activeVerb);
|
|
760
748
|
break;
|
|
749
|
+
case "thinking_delta": {
|
|
750
|
+
liveThinkingPreview = stripTerminalEscapes(`${liveThinkingPreview}${event.text}`)
|
|
751
|
+
.replace(/\s+/g, " ")
|
|
752
|
+
.trimStart()
|
|
753
|
+
.slice(0, 1000);
|
|
754
|
+
if (liveStatus)
|
|
755
|
+
liveStatus.text = "Thinking";
|
|
756
|
+
paintLiveStatus();
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
761
759
|
case "llm_retry":
|
|
762
760
|
startLiveStatus(event.mode === "after-compaction"
|
|
763
761
|
? "Retrying after compaction"
|
|
@@ -1038,6 +1036,9 @@ async function readCliLine(options) {
|
|
|
1038
1036
|
const liveStatusLine = renderLiveStatusLine(options.color);
|
|
1039
1037
|
if (liveStatusLine)
|
|
1040
1038
|
lines.push(liveStatusLine);
|
|
1039
|
+
if (liveStatus?.text === "Thinking" && liveThinkingPreview) {
|
|
1040
|
+
lines.push(...renderLiveThinkingLines(liveThinkingPreview, options.color));
|
|
1041
|
+
}
|
|
1041
1042
|
lines.push(renderInputBorder("top", options.color));
|
|
1042
1043
|
const view = renderInputView(buffer, cursor, inputContentWidth(), pastedTextBlocks);
|
|
1043
1044
|
const idleQueue = options.queueMode && !buffer;
|
|
@@ -1614,6 +1615,37 @@ function renderLiveStatusLine(useColor) {
|
|
|
1614
1615
|
: `${liveStatus.text}...`;
|
|
1615
1616
|
return formatWaitLine({ frame: liveStatus.frame, label, elapsed, useColor });
|
|
1616
1617
|
}
|
|
1618
|
+
function renderLiveThinkingLines(value, useColor) {
|
|
1619
|
+
const prefixWidth = 4;
|
|
1620
|
+
const width = Math.max(24, Math.min((stdout.columns ?? 100) - prefixWidth, 96));
|
|
1621
|
+
const lines = [];
|
|
1622
|
+
let current = "";
|
|
1623
|
+
let currentWidth = 0;
|
|
1624
|
+
let consumed = 0;
|
|
1625
|
+
const characters = Array.from(value);
|
|
1626
|
+
for (const char of characters) {
|
|
1627
|
+
const charWidth = displayLength(char);
|
|
1628
|
+
if (current && currentWidth + charWidth > width) {
|
|
1629
|
+
lines.push(current);
|
|
1630
|
+
if (lines.length === 3)
|
|
1631
|
+
break;
|
|
1632
|
+
current = "";
|
|
1633
|
+
currentWidth = 0;
|
|
1634
|
+
}
|
|
1635
|
+
current += char;
|
|
1636
|
+
currentWidth += charWidth;
|
|
1637
|
+
consumed += 1;
|
|
1638
|
+
}
|
|
1639
|
+
if (lines.length < 3 && current)
|
|
1640
|
+
lines.push(current);
|
|
1641
|
+
if (consumed < characters.length && lines.length) {
|
|
1642
|
+
const last = truncateDisplay(lines[lines.length - 1], Math.max(1, width - 1)).replace(/…$/, "");
|
|
1643
|
+
lines[lines.length - 1] = `${last}…`;
|
|
1644
|
+
}
|
|
1645
|
+
const dim = useColor ? "\x1b[90m" : "";
|
|
1646
|
+
const reset = useColor ? "\x1b[0m" : "";
|
|
1647
|
+
return lines.map((line, index) => `${dim}${index === 0 ? " └ " : " "}${line}${reset}`);
|
|
1648
|
+
}
|
|
1617
1649
|
function renderInputBorder(position, useColor) {
|
|
1618
1650
|
const terminalWidth = Math.max(48, Math.min(stdout.columns ?? 100, 100));
|
|
1619
1651
|
const left = position === "top" ? "╭" : "╰";
|
|
@@ -1641,7 +1673,7 @@ function renderWelcomeBanner(options) {
|
|
|
1641
1673
|
`${dim}trace${reset} ${options.quiet ? "quiet" : "verbose"}`,
|
|
1642
1674
|
`${dim}approval${reset} ${options.yolo ? `${green}yolo${reset}` : "ask"}`,
|
|
1643
1675
|
"",
|
|
1644
|
-
`${dim}Type / to search · /
|
|
1676
|
+
`${dim}Type / to search · /help${reset}`,
|
|
1645
1677
|
"",
|
|
1646
1678
|
"",
|
|
1647
1679
|
].join("\n");
|
package/dist/llm/anthropic.d.ts
CHANGED
|
@@ -18,29 +18,40 @@ export type ToolInputDelta = {
|
|
|
18
18
|
tool?: string;
|
|
19
19
|
detail?: string;
|
|
20
20
|
};
|
|
21
|
+
export type ThinkingDelta = {
|
|
22
|
+
text: string;
|
|
23
|
+
};
|
|
24
|
+
export type CreateMessageInput = {
|
|
25
|
+
system: string;
|
|
26
|
+
messages: Message[];
|
|
27
|
+
tools: AnthropicTool[];
|
|
28
|
+
stream?: boolean;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
onTextDelta?: (text: string) => void;
|
|
31
|
+
onThinkingDelta?: (event: ThinkingDelta) => void;
|
|
32
|
+
onToolInputDelta?: (event: ToolInputDelta) => void;
|
|
33
|
+
};
|
|
34
|
+
export interface LlmClient {
|
|
35
|
+
createMessage(input: CreateMessageInput): Promise<AnthropicResponse>;
|
|
36
|
+
}
|
|
21
37
|
export declare class MalformedStreamError extends Error {
|
|
22
38
|
constructor(message: string, cause?: unknown);
|
|
23
39
|
}
|
|
24
40
|
export declare class MalformedToolInputError extends MalformedStreamError {
|
|
25
41
|
constructor(tool: string, bytes: number, cause: unknown);
|
|
26
42
|
}
|
|
27
|
-
export declare class
|
|
43
|
+
export declare class ProviderApiError extends Error {
|
|
28
44
|
readonly status: number;
|
|
29
45
|
readonly responseBody: string;
|
|
30
46
|
readonly responseHeaders: Headers;
|
|
31
47
|
constructor(status: number, responseBody: string, responseHeaders: Headers);
|
|
32
48
|
}
|
|
49
|
+
export declare class AnthropicApiError extends ProviderApiError {
|
|
50
|
+
name: string;
|
|
51
|
+
}
|
|
33
52
|
export declare class AnthropicClient {
|
|
34
53
|
private readonly config;
|
|
35
54
|
constructor(config: LlmConfig);
|
|
36
|
-
createMessage(input:
|
|
37
|
-
system: string;
|
|
38
|
-
messages: Message[];
|
|
39
|
-
tools: AnthropicTool[];
|
|
40
|
-
stream?: boolean;
|
|
41
|
-
signal?: AbortSignal;
|
|
42
|
-
onTextDelta?: (text: string) => void;
|
|
43
|
-
onToolInputDelta?: (event: ToolInputDelta) => void;
|
|
44
|
-
}): Promise<AnthropicResponse>;
|
|
55
|
+
createMessage(input: CreateMessageInput): Promise<AnthropicResponse>;
|
|
45
56
|
}
|
|
46
57
|
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/llm/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,EAAE,MAAM,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/llm/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B,EAAE,MAAM,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC;AAOF,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/E,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAC7C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACjD,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CACpD,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACtE;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAI7C;AAED,qBAAa,uBAAwB,SAAQ,oBAAoB;gBACnD,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAIxD;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,MAAM,EAAE,MAAM;aACd,YAAY,EAAE,MAAM;aACpB,eAAe,EAAE,OAAO;gBAFxB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,OAAO;CAK3C;AAED,qBAAa,iBAAkB,SAAQ,gBAAgB;IAC5C,IAAI,SAAuB;CACrC;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAExC,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAsC3E"}
|
package/dist/llm/anthropic.js
CHANGED
|
@@ -10,18 +10,21 @@ export class MalformedToolInputError extends MalformedStreamError {
|
|
|
10
10
|
this.name = "MalformedToolInputError";
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
export class
|
|
13
|
+
export class ProviderApiError extends Error {
|
|
14
14
|
status;
|
|
15
15
|
responseBody;
|
|
16
16
|
responseHeaders;
|
|
17
17
|
constructor(status, responseBody, responseHeaders) {
|
|
18
|
-
super(`
|
|
18
|
+
super(`Provider API error ${status}: ${responseBody}`);
|
|
19
19
|
this.status = status;
|
|
20
20
|
this.responseBody = responseBody;
|
|
21
21
|
this.responseHeaders = responseHeaders;
|
|
22
|
-
this.name = "
|
|
22
|
+
this.name = "ProviderApiError";
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
+
export class AnthropicApiError extends ProviderApiError {
|
|
26
|
+
name = "AnthropicApiError";
|
|
27
|
+
}
|
|
25
28
|
export class AnthropicClient {
|
|
26
29
|
config;
|
|
27
30
|
constructor(config) {
|
|
@@ -48,6 +51,7 @@ export class AnthropicClient {
|
|
|
48
51
|
cache_control: this.config.cacheTtl === "1h"
|
|
49
52
|
? { type: "ephemeral", ttl: "1h" }
|
|
50
53
|
: { type: "ephemeral" },
|
|
54
|
+
...(supportsThinking(this.config) ? { thinking: { type: "enabled" } } : {}),
|
|
51
55
|
stream: input.stream ?? false,
|
|
52
56
|
}),
|
|
53
57
|
signal: input.signal
|
|
@@ -59,12 +63,12 @@ export class AnthropicClient {
|
|
|
59
63
|
throw new AnthropicApiError(response.status, errorText, response.headers);
|
|
60
64
|
}
|
|
61
65
|
if (input.stream) {
|
|
62
|
-
return readStreamingResponse(response, input.onTextDelta, input.onToolInputDelta);
|
|
66
|
+
return readStreamingResponse(response, input.onTextDelta, input.onToolInputDelta, input.onThinkingDelta);
|
|
63
67
|
}
|
|
64
68
|
return (await response.json());
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
|
-
async function readStreamingResponse(response, onTextDelta, onToolInputDelta) {
|
|
71
|
+
async function readStreamingResponse(response, onTextDelta, onToolInputDelta, onThinkingDelta) {
|
|
68
72
|
if (!response.body)
|
|
69
73
|
throw new Error("Anthropic API returned an empty stream body.");
|
|
70
74
|
const reader = response.body.getReader();
|
|
@@ -100,7 +104,7 @@ async function readStreamingResponse(response, onTextDelta, onToolInputDelta) {
|
|
|
100
104
|
stopReason = reason;
|
|
101
105
|
}, (part) => {
|
|
102
106
|
usage = mergeUsage(usage, part);
|
|
103
|
-
}, onTextDelta, onToolInputDelta);
|
|
107
|
+
}, onTextDelta, onToolInputDelta, onThinkingDelta);
|
|
104
108
|
}
|
|
105
109
|
separator = buffer.match(/\r?\n\r?\n/);
|
|
106
110
|
}
|
|
@@ -119,7 +123,7 @@ async function readStreamingResponse(response, onTextDelta, onToolInputDelta) {
|
|
|
119
123
|
stopReason = reason;
|
|
120
124
|
}, (part) => {
|
|
121
125
|
usage = mergeUsage(usage, part);
|
|
122
|
-
}, onTextDelta, onToolInputDelta);
|
|
126
|
+
}, onTextDelta, onToolInputDelta, onThinkingDelta);
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
129
|
if (blocks.size === 0) {
|
|
@@ -161,7 +165,7 @@ function parseStreamEvent(data) {
|
|
|
161
165
|
throw new MalformedStreamError("Model returned malformed JSON in the event stream.", error);
|
|
162
166
|
}
|
|
163
167
|
}
|
|
164
|
-
function applyStreamEvent(event, blocks, setResponseId, setStopReason, setUsage, onTextDelta, onToolInputDelta) {
|
|
168
|
+
function applyStreamEvent(event, blocks, setResponseId, setStopReason, setUsage, onTextDelta, onToolInputDelta, onThinkingDelta) {
|
|
165
169
|
if (event.type === "message_start") {
|
|
166
170
|
const message = event.message;
|
|
167
171
|
if (message?.id)
|
|
@@ -186,6 +190,13 @@ function applyStreamEvent(event, blocks, setResponseId, setStopReason, setUsage,
|
|
|
186
190
|
if (contentBlock.type === "text") {
|
|
187
191
|
blocks.set(index, { type: "text", text: contentBlock.text ?? "" });
|
|
188
192
|
}
|
|
193
|
+
else if (contentBlock.type === "thinking") {
|
|
194
|
+
blocks.set(index, {
|
|
195
|
+
type: "thinking",
|
|
196
|
+
thinking: contentBlock.thinking ?? "",
|
|
197
|
+
signature: contentBlock.signature,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
189
200
|
else if (contentBlock.type === "tool_use") {
|
|
190
201
|
const block = {
|
|
191
202
|
type: "tool_use",
|
|
@@ -203,12 +214,26 @@ function applyStreamEvent(event, blocks, setResponseId, setStopReason, setUsage,
|
|
|
203
214
|
const index = Number(event.index);
|
|
204
215
|
const block = blocks.get(index);
|
|
205
216
|
const delta = event.delta;
|
|
206
|
-
if (!
|
|
217
|
+
if (!delta)
|
|
218
|
+
return;
|
|
219
|
+
const publicThinking = delta.type === "summary_delta"
|
|
220
|
+
? delta.summary
|
|
221
|
+
: delta.type === "reasoning_delta" || delta.type === "reasoning_summary_delta"
|
|
222
|
+
? (delta.reasoning_summary ?? delta.reasoning)
|
|
223
|
+
: delta.type === "thinking_delta"
|
|
224
|
+
? delta.thinking
|
|
225
|
+
: undefined;
|
|
226
|
+
if (publicThinking)
|
|
227
|
+
onThinkingDelta?.({ text: publicThinking });
|
|
228
|
+
if (!block)
|
|
207
229
|
return;
|
|
208
230
|
if (block.type === "text" && delta.type === "text_delta" && delta.text) {
|
|
209
231
|
block.text += delta.text;
|
|
210
232
|
onTextDelta?.(delta.text);
|
|
211
233
|
}
|
|
234
|
+
else if (block.type === "thinking" && delta.type === "thinking_delta" && delta.thinking) {
|
|
235
|
+
block.thinking += delta.thinking;
|
|
236
|
+
}
|
|
212
237
|
else if (block.type === "tool_use" && delta.type === "input_json_delta" && delta.partial_json) {
|
|
213
238
|
block.inputJson += delta.partial_json;
|
|
214
239
|
block.detail ??= partialToolDetail(block.inputJson);
|
|
@@ -258,7 +283,7 @@ function mergeUsage(current, part) {
|
|
|
258
283
|
}, { ...next });
|
|
259
284
|
}
|
|
260
285
|
function finalizeStreamBlock(block) {
|
|
261
|
-
if (block.type === "text")
|
|
286
|
+
if (block.type === "text" || block.type === "thinking")
|
|
262
287
|
return block;
|
|
263
288
|
let input = {};
|
|
264
289
|
if (block.inputJson.trim()) {
|
|
@@ -276,3 +301,6 @@ function finalizeStreamBlock(block) {
|
|
|
276
301
|
input,
|
|
277
302
|
};
|
|
278
303
|
}
|
|
304
|
+
function supportsThinking(config) {
|
|
305
|
+
return /(?:^glm-|bigmodel\.cn)/i.test(`${config.model} ${config.baseUrl}`);
|
|
306
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LlmConfig } from "../types.js";
|
|
2
|
+
import { type AnthropicResponse, type CreateMessageInput, type LlmClient } from "./anthropic.js";
|
|
3
|
+
export declare class OpenAIClient implements LlmClient {
|
|
4
|
+
private readonly config;
|
|
5
|
+
constructor(config: LlmConfig);
|
|
6
|
+
createMessage(input: CreateMessageInput): Promise<AnthropicResponse>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/llm/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2C,SAAS,EAAW,MAAM,aAAa,CAAC;AAC/F,OAAO,EAIL,KAAK,iBAAiB,EAEtB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,gBAAgB,CAAC;AAKxB,qBAAa,YAAa,YAAW,SAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,SAAS;IAExC,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAmB3E"}
|