@tenex-chat/backend 0.9.4 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/daemon-wrapper.cjs +47 -0
- package/dist/index.js +59268 -0
- package/dist/wrapper.js +171 -0
- package/package.json +19 -27
- package/src/agents/AgentRegistry.ts +9 -7
- package/src/agents/AgentStorage.ts +24 -1
- package/src/agents/agent-installer.ts +6 -0
- package/src/agents/agent-loader.ts +7 -2
- package/src/agents/constants.ts +10 -2
- package/src/agents/execution/AgentExecutor.ts +35 -6
- package/src/agents/execution/StreamCallbacks.ts +53 -13
- package/src/agents/execution/StreamExecutionHandler.ts +110 -16
- package/src/agents/execution/StreamSetup.ts +19 -9
- package/src/agents/execution/ToolEventHandlers.ts +112 -0
- package/src/agents/role-categories.ts +53 -0
- package/src/agents/types/runtime.ts +7 -0
- package/src/agents/types/storage.ts +7 -0
- package/src/commands/agent/import/openclaw-distiller.ts +63 -7
- package/src/commands/agent/import/openclaw-reader.ts +54 -0
- package/src/commands/agent/import/openclaw.ts +120 -29
- package/src/commands/agent/index.ts +83 -2
- package/src/commands/setup/display.ts +123 -0
- package/src/commands/setup/embed.ts +13 -13
- package/src/commands/setup/global-system-prompt.ts +15 -17
- package/src/commands/setup/image.ts +17 -20
- package/src/commands/setup/interactive.ts +37 -20
- package/src/commands/setup/llm.ts +12 -7
- package/src/commands/setup/onboarding.ts +1580 -248
- package/src/commands/setup/providers.ts +3 -3
- package/src/conversations/ConversationStore.ts +23 -2
- package/src/conversations/MessageBuilder.ts +51 -73
- package/src/conversations/formatters/utils/conversation-transcript-formatter.ts +425 -0
- package/src/conversations/search/embeddings/ConversationEmbeddingService.ts +40 -98
- package/src/conversations/search/embeddings/ConversationIndexingJob.ts +40 -52
- package/src/conversations/services/ConversationSummarizer.ts +1 -2
- package/src/conversations/types.ts +11 -0
- package/src/daemon/Daemon.ts +78 -57
- package/src/daemon/ProjectRuntime.ts +6 -12
- package/src/daemon/SubscriptionManager.ts +13 -0
- package/src/daemon/index.ts +0 -1
- package/src/event-handler/index.ts +1 -0
- package/src/index.ts +20 -1
- package/src/llm/ChunkHandler.ts +1 -1
- package/src/llm/FinishHandler.ts +28 -4
- package/src/llm/LLMConfigEditor.ts +218 -106
- package/src/llm/index.ts +0 -4
- package/src/llm/meta/MetaModelResolver.ts +3 -18
- package/src/llm/middleware/message-sanitizer.ts +153 -0
- package/src/llm/providers/ollama-models.ts +0 -38
- package/src/llm/service.ts +50 -15
- package/src/llm/types.ts +0 -12
- package/src/llm/utils/ConfigurationManager.ts +88 -465
- package/src/llm/utils/ConfigurationTester.ts +42 -185
- package/src/llm/utils/ModelSelector.ts +156 -92
- package/src/llm/utils/ProviderConfigUI.ts +10 -141
- package/src/llm/utils/models-dev-cache.ts +102 -23
- package/src/llm/utils/provider-select-prompt.ts +284 -0
- package/src/llm/utils/provider-setup.ts +81 -34
- package/src/llm/utils/variant-list-prompt.ts +361 -0
- package/src/nostr/AgentEventDecoder.ts +1 -0
- package/src/nostr/AgentEventEncoder.ts +37 -0
- package/src/nostr/AgentProfilePublisher.ts +13 -0
- package/src/nostr/AgentPublisher.ts +26 -0
- package/src/nostr/kinds.ts +1 -0
- package/src/nostr/ndkClient.ts +4 -1
- package/src/nostr/types.ts +12 -0
- package/src/prompts/fragments/25-rag-instructions.ts +22 -21
- package/src/prompts/fragments/31-agents-md-guidance.ts +7 -21
- package/src/prompts/fragments/index.ts +2 -0
- package/src/prompts/utils/systemPromptBuilder.ts +18 -28
- package/src/services/AgentDefinitionMonitor.ts +8 -0
- package/src/services/ConfigService.ts +34 -0
- package/src/services/PubkeyService.ts +7 -1
- package/src/services/compression/CompressionService.ts +133 -74
- package/src/services/compression/compression-utils.ts +110 -19
- package/src/services/config/types.ts +0 -6
- package/src/services/dispatch/AgentDispatchService.ts +79 -0
- package/src/services/intervention/InterventionService.ts +78 -5
- package/src/services/nip46/Nip46SigningService.ts +30 -1
- package/src/services/projects/ProjectContext.ts +8 -6
- package/src/services/rag/RAGCollectionRegistry.ts +199 -0
- package/src/services/rag/RAGDatabaseService.ts +2 -7
- package/src/services/rag/RAGOperations.ts +25 -45
- package/src/services/rag/RAGService.ts +0 -31
- package/src/services/rag/RagSubscriptionService.ts +71 -122
- package/src/services/rag/rag-utils.ts +13 -0
- package/src/services/ral/RALRegistry.ts +25 -184
- package/src/services/reports/ReportEmbeddingService.ts +63 -113
- package/src/services/search/UnifiedSearchService.ts +115 -4
- package/src/services/search/index.ts +1 -0
- package/src/services/search/projectFilter.ts +20 -4
- package/src/services/search/providers/ConversationSearchProvider.ts +1 -0
- package/src/services/search/providers/GenericCollectionSearchProvider.ts +81 -0
- package/src/services/search/providers/LessonSearchProvider.ts +1 -8
- package/src/services/search/providers/ReportSearchProvider.ts +1 -0
- package/src/services/search/types.ts +24 -3
- package/src/services/trust-pubkeys/SystemPubkeyListService.ts +148 -0
- package/src/services/trust-pubkeys/TrustPubkeyService.ts +70 -9
- package/src/telemetry/setup.ts +2 -13
- package/src/tools/implementations/ask.ts +3 -3
- package/src/tools/implementations/conversation_get.ts +28 -268
- package/src/tools/implementations/fs_grep.ts +6 -6
- package/src/tools/implementations/fs_read.ts +2 -0
- package/src/tools/implementations/fs_write.ts +2 -0
- package/src/tools/implementations/learn.ts +38 -50
- package/src/tools/implementations/rag_add_documents.ts +6 -4
- package/src/tools/implementations/rag_create_collection.ts +37 -4
- package/src/tools/implementations/rag_delete_collection.ts +9 -0
- package/src/tools/implementations/{search.ts → rag_search.ts} +31 -25
- package/src/tools/registry.ts +7 -8
- package/src/tools/types.ts +11 -2
- package/src/tools/utils/transcript-args.ts +13 -0
- package/src/utils/cli-theme.ts +13 -0
- package/src/utils/logger.ts +55 -0
- package/src/utils/metadataKeys.ts +17 -0
- package/src/utils/sqlEscaping.ts +39 -0
- package/src/wrapper.ts +7 -3
- package/dist/src/index.js +0 -46778
- package/dist/tenex-backend-wrapper.cjs +0 -3
- package/src/agents/execution/constants.ts +0 -16
- package/src/agents/execution/index.ts +0 -3
- package/src/agents/index.ts +0 -4
- package/src/commands/agent.ts +0 -215
- package/src/conversations/formatters/DelegationXmlFormatter.ts +0 -64
- package/src/conversations/formatters/index.ts +0 -9
- package/src/conversations/index.ts +0 -2
- package/src/conversations/utils/content-utils.ts +0 -69
- package/src/daemon/UnixSocketTransport.ts +0 -318
- package/src/event-handler/newConversation.ts +0 -165
- package/src/events/NDKProjectStatus.ts +0 -384
- package/src/events/index.ts +0 -4
- package/src/lib/json-parser.ts +0 -30
- package/src/llm/RecordingState.ts +0 -37
- package/src/llm/StreamPublisher.ts +0 -40
- package/src/llm/middleware/flight-recorder.ts +0 -188
- package/src/llm/utils/claudeCodePromptCompiler.ts +0 -141
- package/src/nostr/constants.ts +0 -38
- package/src/prompts/core/index.ts +0 -3
- package/src/prompts/index.ts +0 -21
- package/src/services/image/index.ts +0 -12
- package/src/services/status/index.ts +0 -11
- package/src/telemetry/diagnostics.ts +0 -27
- package/src/tools/implementations/rag_query.ts +0 -107
- package/src/types/index.ts +0 -46
- package/src/utils/agentFetcher.ts +0 -107
- package/src/utils/conversation-utils.ts +0 -1
- package/src/utils/process.ts +0 -49
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { LanguageModelMiddleware } from "ai";
|
|
2
|
+
import type {
|
|
3
|
+
LanguageModelV3CallOptions,
|
|
4
|
+
LanguageModelV3Message,
|
|
5
|
+
} from "@ai-sdk/provider";
|
|
6
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { getTenexBasePath } from "@/constants";
|
|
9
|
+
import { trace } from "@opentelemetry/api";
|
|
10
|
+
|
|
11
|
+
interface SanitizationWarning {
|
|
12
|
+
fix: string;
|
|
13
|
+
removed: Array<{ index: number; role: string }>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if a user or assistant message has empty content (content: []).
|
|
18
|
+
* System messages use string content (always valid).
|
|
19
|
+
* Tool messages may legitimately have minimal content for adjacency.
|
|
20
|
+
*/
|
|
21
|
+
function hasEmptyContent(msg: LanguageModelV3Message): boolean {
|
|
22
|
+
if (msg.role === "system" || msg.role === "tool") return false;
|
|
23
|
+
return Array.isArray(msg.content) && msg.content.length === 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Run all sanitization passes on the original prompt, collecting warnings
|
|
28
|
+
* with indices in the original coordinate space.
|
|
29
|
+
*
|
|
30
|
+
* Pass 1: Mark empty-content user/assistant messages for removal.
|
|
31
|
+
* Pass 2: Mark trailing assistant messages for removal (from the end,
|
|
32
|
+
* skipping already-marked indices).
|
|
33
|
+
*
|
|
34
|
+
* Returns the filtered array and any warnings.
|
|
35
|
+
*/
|
|
36
|
+
function sanitize(
|
|
37
|
+
prompt: LanguageModelV3Message[]
|
|
38
|
+
): { result: LanguageModelV3Message[]; warnings: SanitizationWarning[] } {
|
|
39
|
+
const warnings: SanitizationWarning[] = [];
|
|
40
|
+
const removeSet = new Set<number>();
|
|
41
|
+
|
|
42
|
+
// Pass 1: empty content
|
|
43
|
+
const emptyRemoved: Array<{ index: number; role: string }> = [];
|
|
44
|
+
for (let i = 0; i < prompt.length; i++) {
|
|
45
|
+
if (hasEmptyContent(prompt[i])) {
|
|
46
|
+
emptyRemoved.push({ index: i, role: prompt[i].role });
|
|
47
|
+
removeSet.add(i);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (emptyRemoved.length > 0) {
|
|
51
|
+
warnings.push({ fix: "empty-content-stripped", removed: emptyRemoved });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Pass 2: trailing assistants (walk backwards, skipping already-removed)
|
|
55
|
+
const trailingRemoved: Array<{ index: number; role: string }> = [];
|
|
56
|
+
for (let i = prompt.length - 1; i >= 0; i--) {
|
|
57
|
+
if (removeSet.has(i)) continue;
|
|
58
|
+
if (prompt[i].role !== "assistant") break;
|
|
59
|
+
trailingRemoved.push({ index: i, role: "assistant" });
|
|
60
|
+
removeSet.add(i);
|
|
61
|
+
}
|
|
62
|
+
if (trailingRemoved.length > 0) {
|
|
63
|
+
warnings.push({ fix: "trailing-assistant-stripped", removed: trailingRemoved });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Build filtered result
|
|
67
|
+
const result = prompt.filter((_, i) => !removeSet.has(i));
|
|
68
|
+
return { result, warnings };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Append a structured warning to $TENEX_BASE_DIR/daemon/warn.log.
|
|
73
|
+
* Uses appendFileSync — this path is rare (only on detected problems)
|
|
74
|
+
* so the sync I/O cost is acceptable vs. the complexity of async.
|
|
75
|
+
*/
|
|
76
|
+
function logWarning(entry: Record<string, unknown>): void {
|
|
77
|
+
try {
|
|
78
|
+
const dir = join(getTenexBasePath(), "daemon");
|
|
79
|
+
mkdirSync(dir, { recursive: true });
|
|
80
|
+
const logPath = join(dir, "warn.log");
|
|
81
|
+
appendFileSync(logPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
82
|
+
} catch {
|
|
83
|
+
// Best-effort logging — never let warn logging crash the LLM call
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Creates a message sanitizer middleware that runs before every LLM API call.
|
|
89
|
+
*
|
|
90
|
+
* This middleware fixes message array problems that would cause API rejection:
|
|
91
|
+
* - Trailing assistant messages (Anthropic rejects these)
|
|
92
|
+
* - Empty-content user/assistant messages
|
|
93
|
+
*
|
|
94
|
+
* It intercepts `params.prompt` via `transformParams`, covering all call paths:
|
|
95
|
+
* initial streamText, prepareStep-rebuilt messages, generateText, and generateObject.
|
|
96
|
+
*
|
|
97
|
+
* When fixes are applied, it logs a structured warning to $TENEX_BASE_DIR/daemon/warn.log
|
|
98
|
+
* and adds an OTel span event for telemetry correlation.
|
|
99
|
+
*/
|
|
100
|
+
export function createMessageSanitizerMiddleware(): LanguageModelMiddleware {
|
|
101
|
+
return {
|
|
102
|
+
specificationVersion: "v3" as const,
|
|
103
|
+
|
|
104
|
+
transformParams: async ({ params, type, model }) => {
|
|
105
|
+
const originalPrompt = params.prompt as LanguageModelV3Message[];
|
|
106
|
+
const originalCount = originalPrompt.length;
|
|
107
|
+
|
|
108
|
+
const { result: sanitized, warnings } = sanitize(originalPrompt);
|
|
109
|
+
|
|
110
|
+
// No fixes needed — return params unchanged
|
|
111
|
+
if (warnings.length === 0) {
|
|
112
|
+
return params;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Build log entry
|
|
116
|
+
const modelId = `${model.provider}:${model.modelId}`;
|
|
117
|
+
const allRemoved = warnings.flatMap((w) => w.removed);
|
|
118
|
+
|
|
119
|
+
for (const warning of warnings) {
|
|
120
|
+
const entry = {
|
|
121
|
+
ts: new Date().toISOString(),
|
|
122
|
+
type: "message-sanitizer",
|
|
123
|
+
fix: warning.fix,
|
|
124
|
+
model: modelId,
|
|
125
|
+
callType: type,
|
|
126
|
+
original_count: originalCount,
|
|
127
|
+
fixed_count: sanitized.length,
|
|
128
|
+
removed: warning.removed,
|
|
129
|
+
};
|
|
130
|
+
logWarning(entry);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// OTel span event — skip gracefully if no active span
|
|
134
|
+
const span = trace.getActiveSpan();
|
|
135
|
+
if (span) {
|
|
136
|
+
span.addEvent("message-sanitizer.fix-applied", {
|
|
137
|
+
"sanitizer.fixes": warnings.map((w) => w.fix).join(","),
|
|
138
|
+
"sanitizer.original_count": originalCount,
|
|
139
|
+
"sanitizer.fixed_count": sanitized.length,
|
|
140
|
+
"sanitizer.removed_indices": allRemoved.map((r) => r.index).join(","),
|
|
141
|
+
"sanitizer.removed_roles": allRemoved.map((r) => r.role).join(","),
|
|
142
|
+
"sanitizer.model": modelId,
|
|
143
|
+
"sanitizer.call_type": type,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
...params,
|
|
149
|
+
prompt: sanitized as LanguageModelV3CallOptions["prompt"],
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -65,41 +65,3 @@ function formatSize(bytes: number): string {
|
|
|
65
65
|
const mb = bytes / (1024 * 1024);
|
|
66
66
|
return `${mb.toFixed(0)}MB`;
|
|
67
67
|
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Get popular Ollama models for quick selection
|
|
71
|
-
*/
|
|
72
|
-
export function getPopularOllamaModels(): Record<string, string[]> {
|
|
73
|
-
return {
|
|
74
|
-
"Small Models (< 4GB)": [
|
|
75
|
-
"llama3.2:1b",
|
|
76
|
-
"llama3.2:3b",
|
|
77
|
-
"phi3:mini",
|
|
78
|
-
"gemma2:2b",
|
|
79
|
-
"qwen2.5:1.5b",
|
|
80
|
-
"qwen2.5:3b",
|
|
81
|
-
],
|
|
82
|
-
"Medium Models (4-8GB)": [
|
|
83
|
-
"llama3.1:8b",
|
|
84
|
-
"mistral:7b",
|
|
85
|
-
"gemma2:9b",
|
|
86
|
-
"qwen2.5:7b",
|
|
87
|
-
"deepseek-coder-v2:16b",
|
|
88
|
-
],
|
|
89
|
-
"Large Models (> 8GB)": [
|
|
90
|
-
"llama3.1:70b",
|
|
91
|
-
"mixtral:8x7b",
|
|
92
|
-
"qwen2.5:14b",
|
|
93
|
-
"qwen2.5:32b",
|
|
94
|
-
"qwen2.5:72b",
|
|
95
|
-
"deepseek-coder-v2:236b",
|
|
96
|
-
],
|
|
97
|
-
"Specialized Models": [
|
|
98
|
-
"codellama:7b",
|
|
99
|
-
"codellama:13b",
|
|
100
|
-
"starcoder2:3b",
|
|
101
|
-
"starcoder2:7b",
|
|
102
|
-
"sqlcoder:7b",
|
|
103
|
-
],
|
|
104
|
-
};
|
|
105
|
-
}
|
package/src/llm/service.ts
CHANGED
|
@@ -22,7 +22,7 @@ import type { z } from "zod";
|
|
|
22
22
|
import { ChunkHandler, type ChunkHandlerState } from "./ChunkHandler";
|
|
23
23
|
import { createFinishHandler, type FinishHandlerConfig, type FinishHandlerState } from "./FinishHandler";
|
|
24
24
|
import { extractLastUserMessage, extractSystemContent, prepareMessagesForRequest } from "./MessageProcessor";
|
|
25
|
-
import {
|
|
25
|
+
import { createMessageSanitizerMiddleware } from "./middleware/message-sanitizer";
|
|
26
26
|
import { PROVIDER_IDS } from "./providers/provider-ids";
|
|
27
27
|
import { getFullTelemetryConfig, getOpenRouterMetadata, getTraceCorrelationId } from "./TracingUtils";
|
|
28
28
|
import type { ProviderCapabilities } from "./providers/types";
|
|
@@ -196,11 +196,20 @@ export class LLMService extends EventEmitter<LLMServiceEventMap> {
|
|
|
196
196
|
throw new Error("No provider available for model creation");
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
return this.wrapWithMiddleware(baseModel);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Wrap a base model with the standard middleware chain.
|
|
204
|
+
* Used by both getLanguageModel() and createLanguageModelFromRegistry()
|
|
205
|
+
* so all call paths get sanitization and reasoning extraction.
|
|
206
|
+
*/
|
|
207
|
+
private wrapWithMiddleware(baseModel: LanguageModel): LanguageModel {
|
|
200
208
|
const middlewares: LanguageModelMiddleware[] = [];
|
|
201
209
|
|
|
202
|
-
//
|
|
203
|
-
|
|
210
|
+
// Message sanitizer — fix message array issues before every API call
|
|
211
|
+
// Must be first so it sanitizes before anything else processes messages
|
|
212
|
+
middlewares.push(createMessageSanitizerMiddleware());
|
|
204
213
|
|
|
205
214
|
// Extract reasoning from thinking tags
|
|
206
215
|
middlewares.push(
|
|
@@ -211,15 +220,10 @@ export class LLMService extends EventEmitter<LLMServiceEventMap> {
|
|
|
211
220
|
})
|
|
212
221
|
);
|
|
213
222
|
|
|
214
|
-
|
|
215
|
-
// Note: Type assertion needed because AI SDK v6 beta uses LanguageModelV3 internally
|
|
216
|
-
// but stable providers export LanguageModel (union type). This is a beta compatibility issue.
|
|
217
|
-
const wrappedModel = wrapLanguageModel({
|
|
223
|
+
return wrapLanguageModel({
|
|
218
224
|
model: baseModel as Parameters<typeof wrapLanguageModel>[0]["model"],
|
|
219
225
|
middleware: middlewares,
|
|
220
226
|
});
|
|
221
|
-
|
|
222
|
-
return wrappedModel;
|
|
223
227
|
}
|
|
224
228
|
|
|
225
229
|
async stream(
|
|
@@ -434,7 +438,7 @@ export class LLMService extends EventEmitter<LLMServiceEventMap> {
|
|
|
434
438
|
// tool-call, tool-input-start, tool-input-delta, tool-result, raw.
|
|
435
439
|
// Events like "finish" and "tool-error" must be forwarded explicitly.
|
|
436
440
|
if (part.type === "finish") {
|
|
437
|
-
// Emit raw-chunk directly
|
|
441
|
+
// Emit raw-chunk directly for low-level listeners.
|
|
438
442
|
// WITHOUT going through ChunkHandler which would trigger chunk-type-change
|
|
439
443
|
// and cause AgentExecutor to publish a duplicate kind:1 event.
|
|
440
444
|
this.emit("raw-chunk", { chunk: part as TextStreamPart<Record<string, AISdkTool>> });
|
|
@@ -516,6 +520,22 @@ export class LLMService extends EventEmitter<LLMServiceEventMap> {
|
|
|
516
520
|
"stream.finish_part_seen": finishPartSeen,
|
|
517
521
|
"stream.abort_signal_aborted": options?.abortSignal?.aborted ?? false,
|
|
518
522
|
});
|
|
523
|
+
logger.writeToWarnLog({
|
|
524
|
+
timestamp: new Date().toISOString(),
|
|
525
|
+
level: "error",
|
|
526
|
+
component: "LLMService",
|
|
527
|
+
message: "LLM stream loop error",
|
|
528
|
+
context: {
|
|
529
|
+
provider: this.provider,
|
|
530
|
+
model: this.model,
|
|
531
|
+
chunkCount,
|
|
532
|
+
lastChunkType: lastChunkType ?? "none",
|
|
533
|
+
finishPartSeen,
|
|
534
|
+
abortSignalAborted: options?.abortSignal?.aborted ?? false,
|
|
535
|
+
},
|
|
536
|
+
error: error instanceof Error ? error.message : String(error),
|
|
537
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
538
|
+
});
|
|
519
539
|
await this.handleStreamError(error, startTime);
|
|
520
540
|
throw error;
|
|
521
541
|
}
|
|
@@ -549,15 +569,17 @@ export class LLMService extends EventEmitter<LLMServiceEventMap> {
|
|
|
549
569
|
}
|
|
550
570
|
|
|
551
571
|
/**
|
|
552
|
-
* Create a language model
|
|
553
|
-
*
|
|
572
|
+
* Create a wrapped language model for dynamic model switching.
|
|
573
|
+
* Wraps the registry model with the same middleware chain
|
|
574
|
+
* (sanitizer, reasoning extraction) used by all other call paths.
|
|
554
575
|
*/
|
|
555
|
-
|
|
576
|
+
createLanguageModelFromRegistry(
|
|
556
577
|
provider: string,
|
|
557
578
|
model: string,
|
|
558
579
|
registry: ProviderRegistryProvider
|
|
559
580
|
): LanguageModel {
|
|
560
|
-
|
|
581
|
+
const baseModel = registry.languageModel(`${provider}:${model}`);
|
|
582
|
+
return this.wrapWithMiddleware(baseModel);
|
|
561
583
|
}
|
|
562
584
|
|
|
563
585
|
/**
|
|
@@ -707,6 +729,19 @@ export class LLMService extends EventEmitter<LLMServiceEventMap> {
|
|
|
707
729
|
duration,
|
|
708
730
|
error: error instanceof Error ? error.message : String(error),
|
|
709
731
|
});
|
|
732
|
+
logger.writeToWarnLog({
|
|
733
|
+
timestamp: new Date().toISOString(),
|
|
734
|
+
level: "error",
|
|
735
|
+
component: "LLMService",
|
|
736
|
+
message: `${operationName} failed`,
|
|
737
|
+
context: {
|
|
738
|
+
provider: this.provider,
|
|
739
|
+
model: this.model,
|
|
740
|
+
duration,
|
|
741
|
+
},
|
|
742
|
+
error: error instanceof Error ? error.message : String(error),
|
|
743
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
744
|
+
});
|
|
710
745
|
throw error;
|
|
711
746
|
}
|
|
712
747
|
}
|
package/src/llm/types.ts
CHANGED
|
@@ -47,18 +47,6 @@ export type LanguageModelUsageWithCostUsd = LanguageModelUsage & {
|
|
|
47
47
|
contextWindow?: number;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
* Chunk sent over local streaming socket
|
|
52
|
-
*/
|
|
53
|
-
export interface LocalStreamChunk {
|
|
54
|
-
/** Hex pubkey of the agent generating this response */
|
|
55
|
-
agent_pubkey: string;
|
|
56
|
-
/** Root event ID of the conversation (hex) */
|
|
57
|
-
conversation_id: string;
|
|
58
|
-
/** Raw AI SDK chunk - passthrough without transformation */
|
|
59
|
-
data: unknown;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
50
|
/**
|
|
63
51
|
* Callback invoked when message injection completes
|
|
64
52
|
* @param delivered - true if the message was successfully delivered to the stream
|