wave-agent-sdk 1.0.0 → 1.0.1
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/dist/agent.d.ts +9 -20
- package/dist/agent.js +23 -97
- package/dist/managers/aiManager.d.ts +63 -8
- package/dist/managers/aiManager.js +270 -80
- package/dist/managers/messageManager.d.ts +9 -5
- package/dist/managers/messageManager.js +36 -12
- package/dist/managers/subagentManager.d.ts +6 -0
- package/dist/managers/subagentManager.js +33 -22
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +0 -4
- package/dist/services/aiService.d.ts +1 -34
- package/dist/services/aiService.js +18 -130
- package/dist/services/autoMemoryService.d.ts +27 -2
- package/dist/services/autoMemoryService.js +124 -36
- package/dist/services/configurationService.js +14 -1
- package/dist/services/session.d.ts +13 -0
- package/dist/services/session.js +64 -0
- package/dist/types/agent.d.ts +0 -2
- package/dist/types/config.d.ts +7 -0
- package/dist/types/core.d.ts +1 -1
- package/dist/utils/containerSetup.js +12 -3
- package/package.json +1 -1
- package/src/agent.ts +36 -110
- package/src/managers/aiManager.ts +366 -105
- package/src/managers/messageManager.ts +51 -23
- package/src/managers/subagentManager.ts +36 -25
- package/src/prompts/index.ts +0 -4
- package/src/services/aiService.ts +25 -203
- package/src/services/autoMemoryService.ts +145 -39
- package/src/services/configurationService.ts +16 -1
- package/src/services/session.ts +68 -0
- package/src/types/agent.ts +0 -6
- package/src/types/config.ts +7 -0
- package/src/types/core.ts +1 -1
- package/src/utils/containerSetup.ts +12 -4
- package/dist/constants/goalPrompts.d.ts +0 -1
- package/dist/constants/goalPrompts.js +0 -10
- package/dist/managers/goalManager.d.ts +0 -42
- package/dist/managers/goalManager.js +0 -177
- package/src/constants/goalPrompts.ts +0 -10
- package/src/managers/goalManager.ts +0 -232
|
@@ -37,7 +37,6 @@ import { READ_TOOL_NAME } from "../constants/tools.js";
|
|
|
37
37
|
import { Container } from "../utils/container.js";
|
|
38
38
|
|
|
39
39
|
export interface MessageManagerCallbacks {
|
|
40
|
-
onMessagesChange?: (messages: Message[]) => void;
|
|
41
40
|
onSessionIdChange?: (sessionId: string) => void;
|
|
42
41
|
onLatestTotalTokensChange?: (latestTotalTokens: number) => void;
|
|
43
42
|
onUsagesChange?: (usages: Usage[]) => void;
|
|
@@ -64,9 +63,17 @@ export interface MessageManagerCallbacks {
|
|
|
64
63
|
onCompactBlockAdded?: (content: string) => void;
|
|
65
64
|
onCompactionStateChange?: (isCompacting: boolean) => void;
|
|
66
65
|
// Bang callback
|
|
67
|
-
onAddBangMessage?: (command: string) => void;
|
|
68
|
-
onUpdateBangMessage?: (
|
|
69
|
-
|
|
66
|
+
onAddBangMessage?: (command: string, messageId: string) => void;
|
|
67
|
+
onUpdateBangMessage?: (
|
|
68
|
+
command: string,
|
|
69
|
+
output: string,
|
|
70
|
+
messageId: string,
|
|
71
|
+
) => void;
|
|
72
|
+
onCompleteBangMessage?: (
|
|
73
|
+
command: string,
|
|
74
|
+
exitCode: number,
|
|
75
|
+
messageId: string,
|
|
76
|
+
) => void;
|
|
70
77
|
onInfoBlockAdded?: (content: string) => void;
|
|
71
78
|
// Rewind callbacks
|
|
72
79
|
onShowRewind?: () => void;
|
|
@@ -322,8 +329,6 @@ export class MessageManager {
|
|
|
322
329
|
this.extractFileReadsFromMessage(messages[messages.length - 1]);
|
|
323
330
|
this.extractSkillInvocationsFromMessage(messages[messages.length - 1]);
|
|
324
331
|
}
|
|
325
|
-
|
|
326
|
-
this.callbacks.onMessagesChange?.([...messages]);
|
|
327
332
|
}
|
|
328
333
|
|
|
329
334
|
/**
|
|
@@ -339,6 +344,20 @@ export class MessageManager {
|
|
|
339
344
|
return;
|
|
340
345
|
}
|
|
341
346
|
|
|
347
|
+
// CC-aligned lazy materialization: when the session file has not been
|
|
348
|
+
// materialized yet (no saved messages) and every unsaved message is a
|
|
349
|
+
// meta message (isMeta: true, e.g. SessionStart hook context), skip
|
|
350
|
+
// persistence. Persisting meta-only sessions would create "0 tokens /
|
|
351
|
+
// No content" ghost entries in the resume list. The meta messages stay
|
|
352
|
+
// in memory and are flushed together with the first real user/assistant
|
|
353
|
+
// message (matches CC's pendingEntries buffering).
|
|
354
|
+
if (
|
|
355
|
+
this.savedMessageCount === 0 &&
|
|
356
|
+
unsavedMessages.every((m) => m.isMeta)
|
|
357
|
+
) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
342
361
|
// Create session if needed (only when we have messages to save)
|
|
343
362
|
if (this.savedMessageCount === 0) {
|
|
344
363
|
// This is the first time saving messages, so create the session
|
|
@@ -622,7 +641,9 @@ export class MessageManager {
|
|
|
622
641
|
command,
|
|
623
642
|
});
|
|
624
643
|
this.setMessages(updatedMessages);
|
|
625
|
-
|
|
644
|
+
// The bang message is appended as the last message
|
|
645
|
+
const messageId = this.messages[this.messages.length - 1]?.id ?? "";
|
|
646
|
+
this.callbacks.onAddBangMessage?.(command, messageId);
|
|
626
647
|
}
|
|
627
648
|
|
|
628
649
|
public updateBangMessage(command: string, output: string): void {
|
|
@@ -632,7 +653,8 @@ export class MessageManager {
|
|
|
632
653
|
output,
|
|
633
654
|
});
|
|
634
655
|
this.setMessages(updatedMessages);
|
|
635
|
-
this.
|
|
656
|
+
const messageId = this.findBangMessageId(command) ?? "";
|
|
657
|
+
this.callbacks.onUpdateBangMessage?.(command, output, messageId);
|
|
636
658
|
}
|
|
637
659
|
|
|
638
660
|
public completeBangMessage(
|
|
@@ -647,7 +669,25 @@ export class MessageManager {
|
|
|
647
669
|
output,
|
|
648
670
|
});
|
|
649
671
|
this.setMessages(updatedMessages);
|
|
650
|
-
this.
|
|
672
|
+
const messageId = this.findBangMessageId(command) ?? "";
|
|
673
|
+
this.callbacks.onCompleteBangMessage?.(command, exitCode, messageId);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Find the message ID of the most recent message containing a bang block
|
|
678
|
+
* for the given command. Bang callbacks do not carry a block ID, so the
|
|
679
|
+
* message is located by matching the command against bang blocks.
|
|
680
|
+
*/
|
|
681
|
+
private findBangMessageId(command: string): string | undefined {
|
|
682
|
+
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
683
|
+
const message = this.messages[i];
|
|
684
|
+
if (message.role !== "user") continue;
|
|
685
|
+
const hasBangBlock = message.blocks?.some(
|
|
686
|
+
(block) => block.type === "bang" && block.command === command,
|
|
687
|
+
);
|
|
688
|
+
if (hasBangBlock) return message.id;
|
|
689
|
+
}
|
|
690
|
+
return undefined;
|
|
651
691
|
}
|
|
652
692
|
|
|
653
693
|
public addNotificationMessage(
|
|
@@ -700,7 +740,6 @@ export class MessageManager {
|
|
|
700
740
|
/**
|
|
701
741
|
* Finalize a streaming block of the given type by setting its stage to "end".
|
|
702
742
|
* Fires the corresponding incremental callback with chunk="" to signal finalization.
|
|
703
|
-
* Does NOT call onMessagesChange — the caller is responsible for that.
|
|
704
743
|
* Returns true if a block was finalized.
|
|
705
744
|
*/
|
|
706
745
|
private finalizeStreamingBlock(
|
|
@@ -807,8 +846,6 @@ export class MessageManager {
|
|
|
807
846
|
});
|
|
808
847
|
|
|
809
848
|
// Note: Subagent-specific callbacks are now handled by SubagentManager
|
|
810
|
-
|
|
811
|
-
this.callbacks.onMessagesChange?.([...this.messages]); // Still need to notify of changes
|
|
812
849
|
}
|
|
813
850
|
|
|
814
851
|
/**
|
|
@@ -871,8 +908,6 @@ export class MessageManager {
|
|
|
871
908
|
accumulated: newAccumulatedReasoning,
|
|
872
909
|
stage: "streaming",
|
|
873
910
|
});
|
|
874
|
-
|
|
875
|
-
this.callbacks.onMessagesChange?.([...this.messages]); // Still need to notify of changes
|
|
876
911
|
}
|
|
877
912
|
|
|
878
913
|
/**
|
|
@@ -885,15 +920,8 @@ export class MessageManager {
|
|
|
885
920
|
const lastMessage = this.messages[this.messages.length - 1];
|
|
886
921
|
if (lastMessage.role !== "assistant") return;
|
|
887
922
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
lastMessage,
|
|
891
|
-
"reasoning",
|
|
892
|
-
);
|
|
893
|
-
|
|
894
|
-
if (textFinalized || reasoningFinalized) {
|
|
895
|
-
this.callbacks.onMessagesChange?.([...this.messages]);
|
|
896
|
-
}
|
|
923
|
+
this.finalizeStreamingBlock(lastMessage, "text");
|
|
924
|
+
this.finalizeStreamingBlock(lastMessage, "reasoning");
|
|
897
925
|
}
|
|
898
926
|
|
|
899
927
|
/**
|
|
@@ -790,6 +790,7 @@ export class SubagentManager {
|
|
|
790
790
|
private createSubagentCallbacks(subagentId: string) {
|
|
791
791
|
return {
|
|
792
792
|
onUserMessageAdded: (params: UserMessageParams) => {
|
|
793
|
+
this.refreshSubagentState(subagentId);
|
|
793
794
|
// Forward user message events to parent via SubagentManager callbacks
|
|
794
795
|
if (this.callbacks?.onSubagentUserMessageAdded) {
|
|
795
796
|
this.callbacks.onSubagentUserMessageAdded(subagentId, params);
|
|
@@ -797,6 +798,7 @@ export class SubagentManager {
|
|
|
797
798
|
},
|
|
798
799
|
|
|
799
800
|
onAssistantMessageAdded: (messageId: string) => {
|
|
801
|
+
this.refreshSubagentState(subagentId);
|
|
800
802
|
// Forward assistant message events to parent via SubagentManager callbacks
|
|
801
803
|
if (this.callbacks?.onSubagentAssistantMessageAdded) {
|
|
802
804
|
this.callbacks.onSubagentAssistantMessageAdded(subagentId, messageId);
|
|
@@ -809,6 +811,7 @@ export class SubagentManager {
|
|
|
809
811
|
accumulated: string;
|
|
810
812
|
stage: "streaming" | "end";
|
|
811
813
|
}) => {
|
|
814
|
+
this.refreshSubagentState(subagentId);
|
|
812
815
|
// Forward assistant content updates to parent via SubagentManager callbacks
|
|
813
816
|
if (this.callbacks?.onSubagentAssistantContentUpdated) {
|
|
814
817
|
this.callbacks.onSubagentAssistantContentUpdated({
|
|
@@ -823,6 +826,7 @@ export class SubagentManager {
|
|
|
823
826
|
accumulated: string;
|
|
824
827
|
stage: "streaming" | "end";
|
|
825
828
|
}) => {
|
|
829
|
+
this.refreshSubagentState(subagentId);
|
|
826
830
|
// Forward assistant reasoning updates to parent via SubagentManager callbacks
|
|
827
831
|
if (this.callbacks?.onSubagentAssistantReasoningUpdated) {
|
|
828
832
|
this.callbacks.onSubagentAssistantReasoningUpdated({
|
|
@@ -833,6 +837,7 @@ export class SubagentManager {
|
|
|
833
837
|
},
|
|
834
838
|
|
|
835
839
|
onToolBlockUpdated: (params: ToolBlockUpdateCallbackParams) => {
|
|
840
|
+
this.refreshSubagentState(subagentId);
|
|
836
841
|
const instance = this.instances.get(subagentId);
|
|
837
842
|
if (instance) {
|
|
838
843
|
// Log tool execution to file only when finalized
|
|
@@ -852,31 +857,6 @@ export class SubagentManager {
|
|
|
852
857
|
}
|
|
853
858
|
},
|
|
854
859
|
|
|
855
|
-
// These callbacks will be handled by the parent agent
|
|
856
|
-
onMessagesChange: (messages: Message[]) => {
|
|
857
|
-
const instance = this.instances.get(subagentId);
|
|
858
|
-
if (instance) {
|
|
859
|
-
instance.messages = messages;
|
|
860
|
-
// Compute usedTools from messages (last 2 tool blocks)
|
|
861
|
-
const toolBlocks = messages.flatMap(
|
|
862
|
-
(m) => m.blocks?.filter((b) => b.type === "tool") ?? [],
|
|
863
|
-
);
|
|
864
|
-
const last2 = toolBlocks.slice(-2);
|
|
865
|
-
instance.usedTools = last2.map((tb) => ({
|
|
866
|
-
name: tb.name ?? "",
|
|
867
|
-
parameters: tb.parameters ?? "",
|
|
868
|
-
compactParams: tb.compactParams,
|
|
869
|
-
stage: tb.stage,
|
|
870
|
-
}));
|
|
871
|
-
// Trigger the onUpdate callback if provided
|
|
872
|
-
instance.onUpdate?.();
|
|
873
|
-
// Forward subagent message changes to parent via callbacks
|
|
874
|
-
if (this.callbacks?.onSubagentMessagesChange) {
|
|
875
|
-
this.callbacks.onSubagentMessagesChange(subagentId, messages);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
},
|
|
879
|
-
|
|
880
860
|
onLatestTotalTokensChange: (tokens: number) => {
|
|
881
861
|
const instance = this.instances.get(subagentId);
|
|
882
862
|
if (instance) {
|
|
@@ -890,6 +870,7 @@ export class SubagentManager {
|
|
|
890
870
|
},
|
|
891
871
|
|
|
892
872
|
onErrorBlockAdded: (error: string) => {
|
|
873
|
+
this.refreshSubagentState(subagentId);
|
|
893
874
|
const instance = this.instances.get(subagentId);
|
|
894
875
|
if (instance?.logStream) {
|
|
895
876
|
instance.logStream.write(
|
|
@@ -899,4 +880,34 @@ export class SubagentManager {
|
|
|
899
880
|
},
|
|
900
881
|
};
|
|
901
882
|
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Pull the latest messages from the subagent instance's MessageManager and
|
|
886
|
+
* refresh the instance's cached messages, usedTools, onUpdate and the
|
|
887
|
+
* onSubagentMessagesChange forwarding. Triggered by incremental callbacks.
|
|
888
|
+
*/
|
|
889
|
+
private refreshSubagentState(subagentId: string): void {
|
|
890
|
+
const instance = this.instances.get(subagentId);
|
|
891
|
+
if (!instance) return;
|
|
892
|
+
|
|
893
|
+
const messages = instance.messageManager.getMessages();
|
|
894
|
+
instance.messages = messages;
|
|
895
|
+
// Compute usedTools from messages (last 2 tool blocks)
|
|
896
|
+
const toolBlocks = messages.flatMap(
|
|
897
|
+
(m) => m.blocks?.filter((b) => b.type === "tool") ?? [],
|
|
898
|
+
);
|
|
899
|
+
const last2 = toolBlocks.slice(-2);
|
|
900
|
+
instance.usedTools = last2.map((tb) => ({
|
|
901
|
+
name: tb.name ?? "",
|
|
902
|
+
parameters: tb.parameters ?? "",
|
|
903
|
+
compactParams: tb.compactParams,
|
|
904
|
+
stage: tb.stage,
|
|
905
|
+
}));
|
|
906
|
+
// Trigger the onUpdate callback if provided
|
|
907
|
+
instance.onUpdate?.();
|
|
908
|
+
// Forward subagent message changes to parent via callbacks
|
|
909
|
+
if (this.callbacks?.onSubagentMessagesChange) {
|
|
910
|
+
this.callbacks.onSubagentMessagesChange(subagentId, messages);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
902
913
|
}
|
package/src/prompts/index.ts
CHANGED
|
@@ -346,10 +346,6 @@ export function formatCompactSummary(summary: string): string {
|
|
|
346
346
|
}
|
|
347
347
|
|
|
348
348
|
export const WEB_CONTENT_SYSTEM_PROMPT = `You are a helpful assistant that extracts information from web content. The content is provided in Markdown format.`;
|
|
349
|
-
export const BTW_SYSTEM_PROMPT = `You are a helpful assistant. Answer the user's side question based on the conversation history.
|
|
350
|
-
Do NOT say things like "Let me try...", "I'll now...", "Let me check...", or promise to take any action.
|
|
351
|
-
If you don't know the answer, say so - do not offer to look it up or investigate.
|
|
352
|
-
Simply answer the question with the information you have.`;
|
|
353
349
|
|
|
354
350
|
export function buildSystemPrompt(
|
|
355
351
|
basePrompt: string | undefined,
|
|
@@ -25,10 +25,8 @@ import * as path from "path";
|
|
|
25
25
|
|
|
26
26
|
import {
|
|
27
27
|
WEB_CONTENT_SYSTEM_PROMPT,
|
|
28
|
-
BTW_SYSTEM_PROMPT,
|
|
29
28
|
type SystemPromptBlock,
|
|
30
29
|
} from "../prompts/index.js";
|
|
31
|
-
import { GOAL_EVALUATION_SYSTEM_PROMPT } from "../constants/goalPrompts.js";
|
|
32
30
|
|
|
33
31
|
/**
|
|
34
32
|
* Interface for debug data saved during 400 errors
|
|
@@ -148,6 +146,18 @@ function getModelConfig(
|
|
|
148
146
|
return config;
|
|
149
147
|
}
|
|
150
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Effective disable-thinking params for a model config. No default: these
|
|
151
|
+
* params are only sent when the user explicitly configures
|
|
152
|
+
* `models[X].disableThinkingOptions` (an empty object clears them), so a
|
|
153
|
+
* gateway that doesn't understand the params is never hit with them.
|
|
154
|
+
*/
|
|
155
|
+
function effectiveDisableThinkingOptions(
|
|
156
|
+
modelConfig: ModelConfig,
|
|
157
|
+
): Record<string, unknown> | undefined {
|
|
158
|
+
return modelConfig.disableThinkingOptions;
|
|
159
|
+
}
|
|
160
|
+
|
|
151
161
|
export interface CallAgentOptions {
|
|
152
162
|
// Resolved configuration
|
|
153
163
|
gatewayConfig: GatewayConfig;
|
|
@@ -184,6 +194,10 @@ export interface CallAgentOptions {
|
|
|
184
194
|
stage?: "start" | "streaming" | "running" | "end";
|
|
185
195
|
}) => void;
|
|
186
196
|
onReasoningUpdate?: (content: string) => void;
|
|
197
|
+
|
|
198
|
+
// Disable-thinking params for fast-model subagent calls (merged into the
|
|
199
|
+
// request; never used in the agent loop).
|
|
200
|
+
disableThinkingOptions?: Record<string, unknown>;
|
|
187
201
|
}
|
|
188
202
|
|
|
189
203
|
export interface CallAgentResult {
|
|
@@ -238,6 +252,7 @@ export async function callAgent(
|
|
|
238
252
|
onContentUpdate,
|
|
239
253
|
onToolUpdate,
|
|
240
254
|
onReasoningUpdate,
|
|
255
|
+
disableThinkingOptions,
|
|
241
256
|
} = options;
|
|
242
257
|
|
|
243
258
|
// Validate model config at call time
|
|
@@ -321,6 +336,7 @@ export async function callAgent(
|
|
|
321
336
|
const openaiModelConfig = getModelConfig(model || modelConfig.model, {
|
|
322
337
|
max_tokens: resolvedMaxTokens,
|
|
323
338
|
...(modelConfig.options || {}),
|
|
339
|
+
...(disableThinkingOptions ?? {}),
|
|
324
340
|
});
|
|
325
341
|
|
|
326
342
|
// Determine if streaming is needed
|
|
@@ -842,10 +858,17 @@ export async function processWebContent(
|
|
|
842
858
|
? modelConfig.fastModelOptions || {}
|
|
843
859
|
: modelConfig.options || {};
|
|
844
860
|
|
|
861
|
+
// Disable-thinking params only apply to the fast-model override path;
|
|
862
|
+
// the agent-model path is untouched.
|
|
863
|
+
const disableThinking = options.model
|
|
864
|
+
? effectiveDisableThinkingOptions(modelConfig)
|
|
865
|
+
: undefined;
|
|
866
|
+
|
|
845
867
|
const openaiModelConfig = getModelConfig(options.model || modelConfig.model, {
|
|
846
868
|
temperature: 0.1,
|
|
847
869
|
max_tokens: 4096,
|
|
848
870
|
...activeExtraParams,
|
|
871
|
+
...(disableThinking || {}),
|
|
849
872
|
});
|
|
850
873
|
|
|
851
874
|
try {
|
|
@@ -894,204 +917,3 @@ export async function processWebContent(
|
|
|
894
917
|
}
|
|
895
918
|
}
|
|
896
919
|
|
|
897
|
-
export interface BtwOptions {
|
|
898
|
-
// Resolved configuration
|
|
899
|
-
gatewayConfig: GatewayConfig;
|
|
900
|
-
modelConfig: ModelConfig;
|
|
901
|
-
|
|
902
|
-
// Parameters
|
|
903
|
-
messages: ChatCompletionMessageParam[];
|
|
904
|
-
question: string;
|
|
905
|
-
abortSignal?: AbortSignal;
|
|
906
|
-
model?: string;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
export interface BtwResult {
|
|
910
|
-
content: string;
|
|
911
|
-
usage?: {
|
|
912
|
-
prompt_tokens: number;
|
|
913
|
-
completion_tokens: number;
|
|
914
|
-
total_tokens: number;
|
|
915
|
-
};
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
export async function btw(options: BtwOptions): Promise<BtwResult> {
|
|
919
|
-
const { gatewayConfig, modelConfig, messages, question, abortSignal } =
|
|
920
|
-
options;
|
|
921
|
-
|
|
922
|
-
// Validate model config at call time
|
|
923
|
-
validateModelConfig(modelConfig);
|
|
924
|
-
|
|
925
|
-
// Apply global 1 QPS rate limit
|
|
926
|
-
if (
|
|
927
|
-
process.env.NODE_ENV !== "test" ||
|
|
928
|
-
modelConfig.model === "rate-limit-test"
|
|
929
|
-
) {
|
|
930
|
-
await acquireSlot(abortSignal);
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
// Create OpenAI client with injected configuration
|
|
934
|
-
const openai = new OpenAIClient({
|
|
935
|
-
apiKey: gatewayConfig.apiKey,
|
|
936
|
-
baseURL: gatewayConfig.baseURL,
|
|
937
|
-
defaultHeaders: gatewayConfig.defaultHeaders,
|
|
938
|
-
fetchOptions: gatewayConfig.fetchOptions,
|
|
939
|
-
fetch: gatewayConfig.fetch,
|
|
940
|
-
});
|
|
941
|
-
|
|
942
|
-
const openaiModelConfig = getModelConfig(options.model || modelConfig.model, {
|
|
943
|
-
temperature: 0.1,
|
|
944
|
-
max_tokens: 4096,
|
|
945
|
-
...(modelConfig.options || {}),
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
try {
|
|
949
|
-
const response = await openai.chat.completions.create(
|
|
950
|
-
{
|
|
951
|
-
...openaiModelConfig,
|
|
952
|
-
messages: [
|
|
953
|
-
{
|
|
954
|
-
role: "system",
|
|
955
|
-
content: BTW_SYSTEM_PROMPT,
|
|
956
|
-
},
|
|
957
|
-
...messages,
|
|
958
|
-
{
|
|
959
|
-
role: "user",
|
|
960
|
-
content: question,
|
|
961
|
-
},
|
|
962
|
-
],
|
|
963
|
-
},
|
|
964
|
-
{
|
|
965
|
-
signal: abortSignal,
|
|
966
|
-
},
|
|
967
|
-
);
|
|
968
|
-
|
|
969
|
-
const result = response.choices[0]?.message?.content?.trim();
|
|
970
|
-
if (!result) {
|
|
971
|
-
throw new Error(
|
|
972
|
-
"Failed to process side question: Empty response from AI",
|
|
973
|
-
);
|
|
974
|
-
}
|
|
975
|
-
const usage = response.usage
|
|
976
|
-
? {
|
|
977
|
-
prompt_tokens: response.usage.prompt_tokens,
|
|
978
|
-
completion_tokens: response.usage.completion_tokens,
|
|
979
|
-
total_tokens: response.usage.total_tokens,
|
|
980
|
-
}
|
|
981
|
-
: undefined;
|
|
982
|
-
|
|
983
|
-
return {
|
|
984
|
-
content: result,
|
|
985
|
-
usage,
|
|
986
|
-
};
|
|
987
|
-
} catch (error) {
|
|
988
|
-
if ((error as Error).name === "AbortError") {
|
|
989
|
-
logger.info("Side question request was aborted");
|
|
990
|
-
throw new Error("Side question request was aborted");
|
|
991
|
-
}
|
|
992
|
-
logger.error("Failed to process side question:", error);
|
|
993
|
-
throw error;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
export interface EvaluateGoalOptions {
|
|
998
|
-
gatewayConfig: GatewayConfig;
|
|
999
|
-
modelConfig: ModelConfig;
|
|
1000
|
-
model: string;
|
|
1001
|
-
goalCondition: string;
|
|
1002
|
-
messages: ChatCompletionMessageParam[];
|
|
1003
|
-
abortSignal?: AbortSignal;
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
export interface EvaluateGoalResult {
|
|
1007
|
-
content: string;
|
|
1008
|
-
usage?: {
|
|
1009
|
-
prompt_tokens: number;
|
|
1010
|
-
completion_tokens: number;
|
|
1011
|
-
total_tokens: number;
|
|
1012
|
-
};
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
export async function evaluateGoal(
|
|
1016
|
-
options: EvaluateGoalOptions,
|
|
1017
|
-
): Promise<EvaluateGoalResult> {
|
|
1018
|
-
const {
|
|
1019
|
-
gatewayConfig,
|
|
1020
|
-
modelConfig,
|
|
1021
|
-
model,
|
|
1022
|
-
goalCondition,
|
|
1023
|
-
messages,
|
|
1024
|
-
abortSignal,
|
|
1025
|
-
} = options;
|
|
1026
|
-
|
|
1027
|
-
// Create OpenAI client with injected configuration (no rate limiter — bypasses 1 QPS)
|
|
1028
|
-
const openai = new OpenAIClient({
|
|
1029
|
-
apiKey: gatewayConfig.apiKey,
|
|
1030
|
-
baseURL: gatewayConfig.baseURL,
|
|
1031
|
-
defaultHeaders: gatewayConfig.defaultHeaders,
|
|
1032
|
-
fetchOptions: gatewayConfig.fetchOptions,
|
|
1033
|
-
fetch: gatewayConfig.fetch,
|
|
1034
|
-
});
|
|
1035
|
-
|
|
1036
|
-
const openaiModelConfig = getModelConfig(model, {
|
|
1037
|
-
temperature: 0,
|
|
1038
|
-
max_tokens: 200,
|
|
1039
|
-
...(modelConfig.fastModelOptions || {}),
|
|
1040
|
-
});
|
|
1041
|
-
|
|
1042
|
-
// Strip images from messages to reduce token usage (same as compact)
|
|
1043
|
-
const cleanedMessages = messages.map((msg) => {
|
|
1044
|
-
if (Array.isArray(msg.content)) {
|
|
1045
|
-
const textParts = msg.content.filter(
|
|
1046
|
-
(part) => part.type === "text",
|
|
1047
|
-
) as import("openai/resources.js").ChatCompletionContentPartText[];
|
|
1048
|
-
const text = textParts.map((p) => p.text).join("\n");
|
|
1049
|
-
return { ...msg, content: text || "(empty message)" };
|
|
1050
|
-
}
|
|
1051
|
-
return msg;
|
|
1052
|
-
});
|
|
1053
|
-
|
|
1054
|
-
try {
|
|
1055
|
-
const response = await openai.chat.completions.create(
|
|
1056
|
-
{
|
|
1057
|
-
...openaiModelConfig,
|
|
1058
|
-
messages: [
|
|
1059
|
-
{
|
|
1060
|
-
role: "system",
|
|
1061
|
-
content: GOAL_EVALUATION_SYSTEM_PROMPT,
|
|
1062
|
-
},
|
|
1063
|
-
...cleanedMessages,
|
|
1064
|
-
{
|
|
1065
|
-
role: "user",
|
|
1066
|
-
content: `Goal condition: ${goalCondition}\n\nHas this goal been achieved based on the conversation above?`,
|
|
1067
|
-
},
|
|
1068
|
-
],
|
|
1069
|
-
},
|
|
1070
|
-
{
|
|
1071
|
-
signal: abortSignal,
|
|
1072
|
-
},
|
|
1073
|
-
);
|
|
1074
|
-
|
|
1075
|
-
const result = response.choices[0]?.message?.content?.trim();
|
|
1076
|
-
if (!result) {
|
|
1077
|
-
throw new Error("Goal evaluation returned empty response");
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
const usage = response.usage
|
|
1081
|
-
? {
|
|
1082
|
-
prompt_tokens: response.usage.prompt_tokens,
|
|
1083
|
-
completion_tokens: response.usage.completion_tokens,
|
|
1084
|
-
total_tokens: response.usage.total_tokens,
|
|
1085
|
-
}
|
|
1086
|
-
: undefined;
|
|
1087
|
-
|
|
1088
|
-
return { content: result, usage };
|
|
1089
|
-
} catch (error) {
|
|
1090
|
-
if ((error as Error).name === "AbortError") {
|
|
1091
|
-
logger.info("Goal evaluation was aborted");
|
|
1092
|
-
throw new Error("Goal evaluation was aborted");
|
|
1093
|
-
}
|
|
1094
|
-
logger.error("Goal evaluation failed:", error);
|
|
1095
|
-
throw error;
|
|
1096
|
-
}
|
|
1097
|
-
}
|