@tyvm/knowhow 0.0.144 → 0.0.146
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/bin/knowhow.js +22 -20
- package/package.json +1 -1
- package/scripts/migrate-usage-json.js +147 -0
- package/src/agents/base/base.ts +65 -10
- package/src/agents/tools/patch.ts +84 -2
- package/src/agents/tools/textSearch.ts +98 -3
- package/src/chat/CliChatService.ts +16 -0
- package/src/chat/modules/AgentModule.ts +159 -29
- package/src/chat/modules/SessionsModule.ts +13 -5
- package/src/chat/types.ts +15 -0
- package/src/chat.ts +11 -0
- package/src/cli.ts +2 -0
- package/src/clients/anthropic.ts +80 -1
- package/src/clients/contextLimits.ts +40 -0
- package/src/clients/fireworks.ts +3 -0
- package/src/clients/gemini.ts +67 -5
- package/src/clients/http.ts +85 -4
- package/src/clients/index.ts +4 -0
- package/src/clients/openai.ts +186 -35
- package/src/clients/pricing/fireworks.ts +50 -1
- package/src/clients/pricing/index.ts +1 -1
- package/src/clients/pricing/models.ts +3 -1
- package/src/clients/pricing/openai.ts +2 -2
- package/src/clients/pricing/qwen.ts +80 -0
- package/src/clients/qwen.ts +16 -0
- package/src/clients/types.ts +99 -8
- package/src/clients/xai.ts +96 -8
- package/src/commands/agent.ts +29 -1
- package/src/commands/services.ts +49 -3
- package/src/index.ts +9 -0
- package/src/plugins/plugins.ts +16 -0
- package/src/processors/Base64ImageDetector.ts +46 -7
- package/src/processors/GemmaToolProcessor.ts +163 -0
- package/src/processors/TokenCompressor.ts +40 -8
- package/src/processors/ToolResponseCache.ts +17 -8
- package/src/processors/index.ts +1 -0
- package/src/processors/tools/boundedToolResponse.ts +49 -0
- package/src/processors/tools/grepToolResponse.ts +164 -38
- package/src/processors/tools/index.ts +2 -0
- package/src/processors/tools/jqToolResponse.ts +40 -10
- package/src/processors/tools/listStoredToolResponses.ts +51 -10
- package/src/processors/tools/tailToolResponse.ts +85 -17
- package/src/services/AgentSyncFs.ts +37 -0
- package/src/services/LazyToolsService.ts +7 -0
- package/src/services/Mcp.ts +9 -2
- package/src/services/RuntimeReloadService.ts +31 -0
- package/src/services/SessionManager.ts +14 -0
- package/src/services/Tools.ts +14 -0
- package/src/services/TracingService.ts +116 -0
- package/src/services/index.ts +8 -0
- package/src/services/modules/computerUse.ts +216 -0
- package/src/services/modules/index.ts +90 -9
- package/src/services/modules/types.ts +31 -1
- package/src/types.ts +3 -0
- package/src/util/Trace.ts +236 -0
- package/tests/clients/contextLimits.test.ts +4 -0
- package/tests/clients/fireworks.test.ts +65 -0
- package/tests/clients/openai.images.test.ts +105 -0
- package/tests/clients/pricing.test.ts +6 -0
- package/tests/clients/reasoningEffort.test.ts +79 -0
- package/tests/compressor/toolResponseCache.test.ts +2 -1
- package/tests/compressor/usageBase64.json +159 -0
- package/tests/compressor/usageBase64.test.ts +175 -0
- package/tests/manual/clients/completions.test.ts +27 -10
- package/tests/patching/fixtures/transcript-mangles/agent-sync-fs-nested-catch.json +9 -0
- package/tests/patching/fixtures/transcript-mangles/automation-functions-crossed.json +9 -0
- package/tests/patching/fixtures/transcript-mangles/fruit-ninja-confirmed-retirement.json +8 -0
- package/tests/patching/patchFileOutput.test.ts +112 -1
- package/tests/patching/transcriptMangleRegression.test.ts +125 -0
- package/tests/processors/Base64ImageDetector.test.ts +36 -1
- package/tests/processors/TokenCompressor.test.ts +17 -3
- package/tests/processors/tools/boundedRetrievalTools.test.ts +68 -0
- package/tests/processors/tools/grepToolResponse.test.ts +88 -1
- package/tests/unit/plugins/pluginLoading.test.ts +31 -0
- package/tests/unit/services/RuntimeReloadService.test.ts +29 -0
- package/ts_build/package.json +1 -1
- package/ts_build/src/agents/base/base.d.ts +8 -1
- package/ts_build/src/agents/base/base.js +37 -3
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/patch.d.ts +1 -0
- package/ts_build/src/agents/tools/patch.js +35 -2
- package/ts_build/src/agents/tools/patch.js.map +1 -1
- package/ts_build/src/agents/tools/textSearch.js +56 -2
- package/ts_build/src/agents/tools/textSearch.js.map +1 -1
- package/ts_build/src/chat/CliChatService.js +15 -0
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +14 -1
- package/ts_build/src/chat/modules/AgentModule.js +86 -24
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/SessionsModule.js +5 -6
- package/ts_build/src/chat/modules/SessionsModule.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +12 -0
- package/ts_build/src/chat.js +7 -0
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/cli.d.ts +2 -1
- package/ts_build/src/cli.js +1 -0
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +1 -0
- package/ts_build/src/clients/anthropic.js +40 -1
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/contextLimits.js +25 -0
- package/ts_build/src/clients/contextLimits.js.map +1 -1
- package/ts_build/src/clients/fireworks.js +1 -0
- package/ts_build/src/clients/fireworks.js.map +1 -1
- package/ts_build/src/clients/gemini.js +56 -4
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/http.d.ts +2 -0
- package/ts_build/src/clients/http.js +66 -9
- package/ts_build/src/clients/http.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +1 -0
- package/ts_build/src/clients/index.js +4 -0
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/openai.d.ts +6 -1
- package/ts_build/src/clients/openai.js +135 -35
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/fireworks.d.ts +4 -1
- package/ts_build/src/clients/pricing/fireworks.js +33 -1
- package/ts_build/src/clients/pricing/fireworks.js.map +1 -1
- package/ts_build/src/clients/pricing/index.d.ts +1 -1
- package/ts_build/src/clients/pricing/index.js +6 -2
- package/ts_build/src/clients/pricing/index.js.map +1 -1
- package/ts_build/src/clients/pricing/models.d.ts +2 -1
- package/ts_build/src/clients/pricing/models.js +4 -1
- package/ts_build/src/clients/pricing/models.js.map +1 -1
- package/ts_build/src/clients/pricing/openai.js +2 -2
- package/ts_build/src/clients/pricing/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/qwen.d.ts +5 -0
- package/ts_build/src/clients/pricing/qwen.js +30 -0
- package/ts_build/src/clients/pricing/qwen.js.map +1 -0
- package/ts_build/src/clients/qwen.d.ts +4 -0
- package/ts_build/src/clients/qwen.js +15 -0
- package/ts_build/src/clients/qwen.js.map +1 -0
- package/ts_build/src/clients/types.d.ts +16 -1
- package/ts_build/src/clients/types.js +14 -0
- package/ts_build/src/clients/types.js.map +1 -1
- package/ts_build/src/clients/xai.d.ts +2 -0
- package/ts_build/src/clients/xai.js +79 -13
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/commands/agent.js +15 -1
- package/ts_build/src/commands/agent.js.map +1 -1
- package/ts_build/src/commands/services.d.ts +2 -0
- package/ts_build/src/commands/services.js +45 -3
- package/ts_build/src/commands/services.js.map +1 -1
- package/ts_build/src/index.d.ts +2 -0
- package/ts_build/src/index.js +1 -0
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +1 -0
- package/ts_build/src/plugins/plugins.js +14 -0
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/processors/Base64ImageDetector.js +22 -3
- package/ts_build/src/processors/Base64ImageDetector.js.map +1 -1
- package/ts_build/src/processors/GemmaToolProcessor.d.ts +14 -0
- package/ts_build/src/processors/GemmaToolProcessor.js +97 -0
- package/ts_build/src/processors/GemmaToolProcessor.js.map +1 -0
- package/ts_build/src/processors/TokenCompressor.js +15 -6
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/processors/ToolResponseCache.d.ts +3 -3
- package/ts_build/src/processors/ToolResponseCache.js +8 -8
- package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
- package/ts_build/src/processors/index.d.ts +1 -0
- package/ts_build/src/processors/index.js +3 -1
- package/ts_build/src/processors/index.js.map +1 -1
- package/ts_build/src/processors/tools/boundedToolResponse.d.ts +7 -0
- package/ts_build/src/processors/tools/boundedToolResponse.js +27 -0
- package/ts_build/src/processors/tools/boundedToolResponse.js.map +1 -0
- package/ts_build/src/processors/tools/grepToolResponse.d.ts +4 -0
- package/ts_build/src/processors/tools/grepToolResponse.js +101 -25
- package/ts_build/src/processors/tools/grepToolResponse.js.map +1 -1
- package/ts_build/src/processors/tools/index.d.ts +2 -2
- package/ts_build/src/processors/tools/index.js.map +1 -1
- package/ts_build/src/processors/tools/jqToolResponse.d.ts +5 -1
- package/ts_build/src/processors/tools/jqToolResponse.js +26 -9
- package/ts_build/src/processors/tools/jqToolResponse.js.map +1 -1
- package/ts_build/src/processors/tools/listStoredToolResponses.d.ts +7 -1
- package/ts_build/src/processors/tools/listStoredToolResponses.js +39 -9
- package/ts_build/src/processors/tools/listStoredToolResponses.js.map +1 -1
- package/ts_build/src/processors/tools/tailToolResponse.d.ts +5 -0
- package/ts_build/src/processors/tools/tailToolResponse.js +56 -13
- package/ts_build/src/processors/tools/tailToolResponse.js.map +1 -1
- package/ts_build/src/services/AgentSyncFs.js +17 -0
- package/ts_build/src/services/AgentSyncFs.js.map +1 -1
- package/ts_build/src/services/LazyToolsService.d.ts +1 -0
- package/ts_build/src/services/LazyToolsService.js +5 -0
- package/ts_build/src/services/LazyToolsService.js.map +1 -1
- package/ts_build/src/services/Mcp.js +8 -2
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/services/RuntimeReloadService.d.ts +12 -0
- package/ts_build/src/services/RuntimeReloadService.js +26 -0
- package/ts_build/src/services/RuntimeReloadService.js.map +1 -0
- package/ts_build/src/services/SessionManager.js +12 -0
- package/ts_build/src/services/SessionManager.js.map +1 -1
- package/ts_build/src/services/Tools.d.ts +3 -0
- package/ts_build/src/services/Tools.js +5 -0
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/TracingService.d.ts +16 -0
- package/ts_build/src/services/TracingService.js +35 -0
- package/ts_build/src/services/TracingService.js.map +1 -0
- package/ts_build/src/services/index.d.ts +6 -0
- package/ts_build/src/services/index.js +7 -1
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/modules/computerUse.d.ts +163 -0
- package/ts_build/src/services/modules/computerUse.js +3 -0
- package/ts_build/src/services/modules/computerUse.js.map +1 -0
- package/ts_build/src/services/modules/index.d.ts +6 -0
- package/ts_build/src/services/modules/index.js +49 -6
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/services/modules/types.d.ts +7 -0
- package/ts_build/src/types.d.ts +3 -0
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/util/Trace.d.ts +13 -0
- package/ts_build/src/util/Trace.js +107 -0
- package/ts_build/src/util/Trace.js.map +1 -0
- package/ts_build/src/workers/tools/index.d.ts +1 -1
- package/ts_build/tests/clients/contextLimits.test.js +2 -0
- package/ts_build/tests/clients/contextLimits.test.js.map +1 -1
- package/ts_build/tests/clients/fireworks.test.d.ts +1 -0
- package/ts_build/tests/clients/fireworks.test.js +65 -0
- package/ts_build/tests/clients/fireworks.test.js.map +1 -0
- package/ts_build/tests/clients/openai.images.test.d.ts +1 -0
- package/ts_build/tests/clients/openai.images.test.js +94 -0
- package/ts_build/tests/clients/openai.images.test.js.map +1 -0
- package/ts_build/tests/clients/pricing.test.js +4 -0
- package/ts_build/tests/clients/pricing.test.js.map +1 -1
- package/ts_build/tests/clients/reasoningEffort.test.d.ts +1 -0
- package/ts_build/tests/clients/reasoningEffort.test.js +68 -0
- package/ts_build/tests/clients/reasoningEffort.test.js.map +1 -0
- package/ts_build/tests/compressor/toolResponseCache.test.js +1 -1
- package/ts_build/tests/compressor/toolResponseCache.test.js.map +1 -1
- package/ts_build/tests/compressor/usageBase64.test.d.ts +1 -0
- package/ts_build/tests/compressor/usageBase64.test.js +109 -0
- package/ts_build/tests/compressor/usageBase64.test.js.map +1 -0
- package/ts_build/tests/manual/clients/completions.test.js +19 -6
- package/ts_build/tests/manual/clients/completions.test.js.map +1 -1
- package/ts_build/tests/patching/patchFileOutput.test.js +82 -0
- package/ts_build/tests/patching/patchFileOutput.test.js.map +1 -1
- package/ts_build/tests/patching/transcriptMangleRegression.test.d.ts +1 -0
- package/ts_build/tests/patching/transcriptMangleRegression.test.js +104 -0
- package/ts_build/tests/patching/transcriptMangleRegression.test.js.map +1 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js +24 -0
- package/ts_build/tests/processors/Base64ImageDetector.test.js.map +1 -1
- package/ts_build/tests/processors/TokenCompressor.test.js +11 -2
- package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.d.ts +1 -0
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js +57 -0
- package/ts_build/tests/processors/tools/boundedRetrievalTools.test.js.map +1 -0
- package/ts_build/tests/processors/tools/grepToolResponse.test.js +37 -0
- package/ts_build/tests/processors/tools/grepToolResponse.test.js.map +1 -1
- package/ts_build/tests/unit/plugins/pluginLoading.test.js +59 -0
- package/ts_build/tests/unit/plugins/pluginLoading.test.js.map +1 -1
- package/ts_build/tests/unit/services/RuntimeReloadService.test.d.ts +1 -0
- package/ts_build/tests/unit/services/RuntimeReloadService.test.js +26 -0
- package/ts_build/tests/unit/services/RuntimeReloadService.test.js.map +1 -0
- package/x-home.png +0 -0
package/src/types.ts
CHANGED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace.ts — Method / class decorators and `tracify()` helper for the
|
|
3
|
+
* @tyvm/knowhow package.
|
|
4
|
+
*
|
|
5
|
+
* These decorators are zero-cost when no tracer is registered (the default).
|
|
6
|
+
* Once `@tyvm/knowhow-module-tracing` (or any other module) calls
|
|
7
|
+
* `TracingService.register(impl)`, every decorated method starts emitting
|
|
8
|
+
* real spans automatically — no code changes required.
|
|
9
|
+
*
|
|
10
|
+
* API mirrors the backend's `Trace.ts` so patterns learned there carry over:
|
|
11
|
+
*
|
|
12
|
+
* @Trace() — wrap a single method
|
|
13
|
+
* @TraceAll() — wrap every public method on a class
|
|
14
|
+
* tracify(obj, ns) — wrap an already-constructed instance
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* import { Trace, TraceAll, tracify } from '../util/Trace';
|
|
18
|
+
*
|
|
19
|
+
* @TraceAll()
|
|
20
|
+
* export class MessageProcessor {
|
|
21
|
+
* async process(msg: Message) { ... } // → span "MessageProcessor.process"
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* @Trace()
|
|
25
|
+
* async fetchEmbeddings(text: string) { ... } // → span "ClassName.fetchEmbeddings"
|
|
26
|
+
*
|
|
27
|
+
* export const myClient = tracify(new SomeClient(), 'someClient');
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { TracingService } from "../services/TracingService";
|
|
31
|
+
|
|
32
|
+
// ─── Shared span lifecycle helper ─────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
function runWithSpan<T>(
|
|
35
|
+
spanName: string,
|
|
36
|
+
attributes: Record<string, string | number | boolean>,
|
|
37
|
+
fn: () => T
|
|
38
|
+
): T {
|
|
39
|
+
const span = TracingService.startSpan(spanName, attributes);
|
|
40
|
+
|
|
41
|
+
const finish = (err?: unknown) => {
|
|
42
|
+
if (err !== undefined) {
|
|
43
|
+
span.recordError(err);
|
|
44
|
+
}
|
|
45
|
+
span.end();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const result = TracingService.withSpan(span, fn);
|
|
50
|
+
|
|
51
|
+
if (result instanceof Promise) {
|
|
52
|
+
return result.then(
|
|
53
|
+
(value) => {
|
|
54
|
+
finish();
|
|
55
|
+
return value;
|
|
56
|
+
},
|
|
57
|
+
(err) => {
|
|
58
|
+
finish(err);
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
) as unknown as T;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
finish();
|
|
65
|
+
return result;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
finish(err);
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── @Trace() — single-method decorator ──────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export interface TraceOptions {
|
|
75
|
+
/** Override the span name (default: `ClassName.methodName`) */
|
|
76
|
+
spanName?: string;
|
|
77
|
+
/** Additional static attributes attached to every span */
|
|
78
|
+
attributes?: Record<string, string | number | boolean>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @Trace() method decorator — wraps the decorated method in a tracing span.
|
|
83
|
+
*
|
|
84
|
+
* Span name defaults to `ClassName.methodName`. Errors are recorded on the
|
|
85
|
+
* span and re-thrown. Works with both sync and async methods.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* @Trace()
|
|
89
|
+
* async fetchEmbeddings(text: string) { ... }
|
|
90
|
+
*
|
|
91
|
+
* @Trace({ spanName: 'embeddings.fetch', attributes: { 'peer.service': 'openai' } })
|
|
92
|
+
* async fetchEmbeddings(text: string) { ... }
|
|
93
|
+
*/
|
|
94
|
+
export function Trace(options: TraceOptions = {}) {
|
|
95
|
+
return function (
|
|
96
|
+
target: object,
|
|
97
|
+
propertyKey: string,
|
|
98
|
+
descriptor: PropertyDescriptor
|
|
99
|
+
): PropertyDescriptor {
|
|
100
|
+
const originalMethod = descriptor.value as (...args: unknown[]) => unknown;
|
|
101
|
+
const className = (target as any).constructor?.name ?? "Unknown";
|
|
102
|
+
const resolvedSpanName =
|
|
103
|
+
options.spanName ?? `${className}.${propertyKey}`;
|
|
104
|
+
const staticAttrs: Record<string, string | number | boolean> = {
|
|
105
|
+
"code.function": propertyKey,
|
|
106
|
+
"code.namespace": className,
|
|
107
|
+
...options.attributes,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
descriptor.value = function (this: unknown, ...args: unknown[]) {
|
|
111
|
+
return runWithSpan(resolvedSpanName, staticAttrs, () =>
|
|
112
|
+
originalMethod.apply(this, args)
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return descriptor;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ─── @TraceAll() — class decorator ───────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
export interface TraceAllOptions {
|
|
123
|
+
/** Additional static attributes attached to every span */
|
|
124
|
+
attributes?: Record<string, string | number | boolean>;
|
|
125
|
+
/** Method names to skip (in addition to constructor and `_`-prefixed) */
|
|
126
|
+
skip?: string[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @TraceAll() class decorator — wraps every public method on the class in a
|
|
131
|
+
* tracing span named `ClassName.methodName`.
|
|
132
|
+
*
|
|
133
|
+
* Skips the constructor and any methods whose name starts with `_`.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* @TraceAll()
|
|
137
|
+
* export class MessageProcessor {
|
|
138
|
+
* async process(msg: Message) { ... } // → span "MessageProcessor.process"
|
|
139
|
+
* }
|
|
140
|
+
*/
|
|
141
|
+
export function TraceAll(options: TraceAllOptions = {}) {
|
|
142
|
+
const skip = new Set(options.skip ?? []);
|
|
143
|
+
|
|
144
|
+
return function <T extends { new (...args: unknown[]): object }>(
|
|
145
|
+
constructor: T
|
|
146
|
+
): T {
|
|
147
|
+
const className = constructor.name;
|
|
148
|
+
const proto = constructor.prototype as Record<string, unknown>;
|
|
149
|
+
|
|
150
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
151
|
+
if (key === "constructor") continue;
|
|
152
|
+
if (key.startsWith("_")) continue;
|
|
153
|
+
if (skip.has(key)) continue;
|
|
154
|
+
|
|
155
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
|
|
156
|
+
if (!descriptor || typeof descriptor.value !== "function") continue;
|
|
157
|
+
|
|
158
|
+
const originalMethod = descriptor.value as (
|
|
159
|
+
...args: unknown[]
|
|
160
|
+
) => unknown;
|
|
161
|
+
const spanName = `${className}.${key}`;
|
|
162
|
+
const staticAttrs: Record<string, string | number | boolean> = {
|
|
163
|
+
"code.function": key,
|
|
164
|
+
"code.namespace": className,
|
|
165
|
+
...options.attributes,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
descriptor.value = function (this: unknown, ...args: unknown[]) {
|
|
169
|
+
return runWithSpan(spanName, staticAttrs, () =>
|
|
170
|
+
originalMethod.apply(this, args)
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
Object.defineProperty(proto, key, descriptor);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return constructor;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ─── tracify() — instance-level wrapper ──────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* tracify — wraps every method on an already-constructed object instance in a
|
|
185
|
+
* tracing span.
|
|
186
|
+
*
|
|
187
|
+
* Ideal for third-party singletons (SDK clients etc.) that cannot be decorated
|
|
188
|
+
* at the class level.
|
|
189
|
+
*
|
|
190
|
+
* @param instance The object whose methods should be traced.
|
|
191
|
+
* @param namespace Prefix for span names, e.g. "embeddingsClient".
|
|
192
|
+
* @param options Optional extra attributes / skip list.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* export const embeddingsClient = tracify(new OpenAI(), 'openai');
|
|
196
|
+
*/
|
|
197
|
+
export function tracify<T extends object>(
|
|
198
|
+
instance: T,
|
|
199
|
+
namespace: string,
|
|
200
|
+
options: TraceAllOptions = {}
|
|
201
|
+
): T {
|
|
202
|
+
const skip = new Set(options.skip ?? []);
|
|
203
|
+
|
|
204
|
+
return new Proxy(instance, {
|
|
205
|
+
get(target, prop, receiver) {
|
|
206
|
+
const value = Reflect.get(target, prop, receiver);
|
|
207
|
+
const methodName = typeof prop === "string" ? prop : String(prop);
|
|
208
|
+
|
|
209
|
+
// Pass symbols through unwrapped
|
|
210
|
+
if (typeof prop === "symbol") return value;
|
|
211
|
+
|
|
212
|
+
// Skip private / user-skipped names
|
|
213
|
+
if (methodName.startsWith("_") || skip.has(methodName)) return value;
|
|
214
|
+
|
|
215
|
+
// Recursively wrap nested objects (e.g. prisma.user.findMany)
|
|
216
|
+
if (value !== null && typeof value === "object") {
|
|
217
|
+
return tracify(value as object, `${namespace}.${methodName}`, options);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (typeof value !== "function") return value;
|
|
221
|
+
|
|
222
|
+
const spanName = `${namespace}.${methodName}`;
|
|
223
|
+
const staticAttrs: Record<string, string | number | boolean> = {
|
|
224
|
+
"code.function": methodName,
|
|
225
|
+
"code.namespace": namespace,
|
|
226
|
+
...options.attributes,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
return function (this: unknown, ...args: unknown[]) {
|
|
230
|
+
return runWithSpan(spanName, staticAttrs, () =>
|
|
231
|
+
(value as Function).apply(target, args)
|
|
232
|
+
);
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
GeminiTextPricing,
|
|
10
10
|
AnthropicTextPricing,
|
|
11
11
|
XaiTextPricing,
|
|
12
|
+
FireworksTextPricing,
|
|
13
|
+
FireworksEmbeddingPricing,
|
|
12
14
|
} from "../../src/clients/pricing";
|
|
13
15
|
|
|
14
16
|
// Model ids explicitly marked deprecated across all pricing tables. Deprecated
|
|
@@ -21,6 +23,8 @@ const deprecatedModelIds = new Set<string>(
|
|
|
21
23
|
...GeminiTextPricing,
|
|
22
24
|
...AnthropicTextPricing,
|
|
23
25
|
...XaiTextPricing,
|
|
26
|
+
...FireworksTextPricing,
|
|
27
|
+
...FireworksEmbeddingPricing,
|
|
24
28
|
} as Record<string, { deprecated?: boolean }>)
|
|
25
29
|
.filter(([, p]) => p && p.deprecated === true)
|
|
26
30
|
.map(([id]) => id)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import http from "../../src/utils/http";
|
|
2
|
+
import { GenericFireworksClient } from "../../src/clients/fireworks";
|
|
3
|
+
import { FireworksModels } from "../../src/clients/pricing/fireworks";
|
|
4
|
+
|
|
5
|
+
jest.mock("../../src/utils/http", () => ({
|
|
6
|
+
__esModule: true,
|
|
7
|
+
default: { post: jest.fn() },
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
const post = http.post as jest.MockedFunction<typeof http.post>;
|
|
11
|
+
|
|
12
|
+
describe("GenericFireworksClient", () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
post.mockReset();
|
|
15
|
+
post.mockResolvedValue({
|
|
16
|
+
data: {
|
|
17
|
+
choices: [{ message: { role: "assistant", content: "ok" } }],
|
|
18
|
+
model: FireworksModels.DeepseekV4Flash,
|
|
19
|
+
usage: { prompt_tokens: 2, completion_tokens: 1, total_tokens: 3 },
|
|
20
|
+
},
|
|
21
|
+
} as any);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("removes non-Fireworks options and tool return metadata", async () => {
|
|
25
|
+
const client = new GenericFireworksClient("test-key");
|
|
26
|
+
|
|
27
|
+
await client.createChatCompletion({
|
|
28
|
+
model: FireworksModels.DeepseekV4Flash,
|
|
29
|
+
messages: [{ role: "user", content: "hello" }],
|
|
30
|
+
max_tokens: 100,
|
|
31
|
+
reasoning_summary: true,
|
|
32
|
+
long_ttl_cache: true,
|
|
33
|
+
tools: [{
|
|
34
|
+
type: "function",
|
|
35
|
+
function: {
|
|
36
|
+
name: "lookup",
|
|
37
|
+
description: "Look something up",
|
|
38
|
+
parameters: { type: "object", properties: {} },
|
|
39
|
+
returns: { type: "string" },
|
|
40
|
+
} as any,
|
|
41
|
+
}],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(post).toHaveBeenCalledTimes(1);
|
|
45
|
+
const [url, body, requestOptions] = post.mock.calls[0];
|
|
46
|
+
expect(url).toBe("https://api.fireworks.ai/inference/v1/chat/completions");
|
|
47
|
+
expect(requestOptions).toMatchObject({
|
|
48
|
+
headers: { Authorization: "Bearer test-key" },
|
|
49
|
+
});
|
|
50
|
+
expect(body).toMatchObject({
|
|
51
|
+
model: FireworksModels.DeepseekV4Flash,
|
|
52
|
+
messages: [{ role: "user", content: "hello" }],
|
|
53
|
+
max_tokens: 100,
|
|
54
|
+
tool_choice: "auto",
|
|
55
|
+
});
|
|
56
|
+
const serializedBody = JSON.parse(JSON.stringify(body));
|
|
57
|
+
expect(serializedBody).not.toHaveProperty("reasoning_summary");
|
|
58
|
+
expect(serializedBody).not.toHaveProperty("long_ttl_cache");
|
|
59
|
+
expect(serializedBody.tools[0].function).toEqual({
|
|
60
|
+
name: "lookup",
|
|
61
|
+
description: "Look something up",
|
|
62
|
+
parameters: { type: "object", properties: {} },
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { GenericOpenAiClient } from "../../src/clients/openai";
|
|
2
|
+
import { Message } from "../../src/clients/types";
|
|
3
|
+
|
|
4
|
+
describe("GenericOpenAiClient image message conversion", () => {
|
|
5
|
+
const imageUrl = "data:image/jpeg;base64,/9j/4AAQSkZJRg==";
|
|
6
|
+
let client: GenericOpenAiClient;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
client = new GenericOpenAiClient("test-key");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("moves tool image output to a user message for Chat Completions", () => {
|
|
13
|
+
const messages: Message[] = [{
|
|
14
|
+
role: "tool",
|
|
15
|
+
tool_call_id: "call_1",
|
|
16
|
+
name: "loadImageAsBase64",
|
|
17
|
+
content: [{
|
|
18
|
+
type: "image_url",
|
|
19
|
+
image_url: { url: imageUrl, detail: "high" },
|
|
20
|
+
}],
|
|
21
|
+
}];
|
|
22
|
+
|
|
23
|
+
const converted = client.toChatCompletionMessages(messages);
|
|
24
|
+
|
|
25
|
+
expect(converted).toEqual([
|
|
26
|
+
{
|
|
27
|
+
role: "tool",
|
|
28
|
+
tool_call_id: "call_1",
|
|
29
|
+
content: [{
|
|
30
|
+
type: "text",
|
|
31
|
+
text: "Image output attached in the following user message.",
|
|
32
|
+
}],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
role: "user",
|
|
36
|
+
content: [{
|
|
37
|
+
type: "image_url",
|
|
38
|
+
image_url: { url: imageUrl, detail: "high" },
|
|
39
|
+
}],
|
|
40
|
+
},
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("uses input_image parts for the Responses API", () => {
|
|
45
|
+
const converted = client.toResponseContent([
|
|
46
|
+
{ type: "text", text: "Screenshot" },
|
|
47
|
+
{ type: "image_url", image_url: { url: imageUrl, detail: "low" } },
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
expect(converted).toEqual([
|
|
51
|
+
{ type: "input_text", text: "Screenshot" },
|
|
52
|
+
{ type: "input_image", image_url: imageUrl, detail: "low" },
|
|
53
|
+
]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it.each([
|
|
57
|
+
{ type: "audio_url", audio_url: { url: "data:audio/wav;base64,UklGRg==" } },
|
|
58
|
+
{ type: "video_url", video_url: { url: "data:video/mp4;base64,AAAAIGZ0eXA=" } },
|
|
59
|
+
] as const)("rejects unsupported $type Responses API content instead of dropping it", (part) => {
|
|
60
|
+
expect(() => client.toResponseContent([
|
|
61
|
+
{ type: "text", text: "Media" },
|
|
62
|
+
part,
|
|
63
|
+
])).toThrow(
|
|
64
|
+
`OpenAI Responses API conversion does not support ${part.type} content.`
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("attaches a tool image as input_image in a Responses API request", async () => {
|
|
69
|
+
const create = jest.fn().mockResolvedValue({
|
|
70
|
+
id: "resp_1",
|
|
71
|
+
output: [],
|
|
72
|
+
usage: undefined,
|
|
73
|
+
});
|
|
74
|
+
(client as any).client = { responses: { create } };
|
|
75
|
+
|
|
76
|
+
await client.createChatResponse({
|
|
77
|
+
model: "gpt-5.4",
|
|
78
|
+
reasoning_effort: "none",
|
|
79
|
+
messages: [{
|
|
80
|
+
role: "tool",
|
|
81
|
+
tool_call_id: "call_1",
|
|
82
|
+
content: [{
|
|
83
|
+
type: "image_url",
|
|
84
|
+
image_url: { url: imageUrl, detail: "auto" },
|
|
85
|
+
}],
|
|
86
|
+
}],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(create.mock.calls[0][0].input).toEqual([
|
|
90
|
+
{
|
|
91
|
+
type: "function_call_output",
|
|
92
|
+
call_id: "call_1",
|
|
93
|
+
output: "Image output attached in the following user message.",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
role: "user",
|
|
97
|
+
content: [{
|
|
98
|
+
type: "input_image",
|
|
99
|
+
image_url: imageUrl,
|
|
100
|
+
detail: "auto",
|
|
101
|
+
}],
|
|
102
|
+
},
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -30,6 +30,8 @@ import {
|
|
|
30
30
|
GeminiTextPricing,
|
|
31
31
|
AnthropicTextPricing,
|
|
32
32
|
XaiTextPricing,
|
|
33
|
+
FireworksTextPricing,
|
|
34
|
+
FireworksEmbeddingPricing,
|
|
33
35
|
ALL_MODEL_CATALOG,
|
|
34
36
|
XaiImagePricing,
|
|
35
37
|
XaiVideoPricing,
|
|
@@ -61,6 +63,8 @@ describe("Model Pricing Coverage", () => {
|
|
|
61
63
|
...GeminiTextPricing,
|
|
62
64
|
...AnthropicTextPricing,
|
|
63
65
|
...XaiTextPricing,
|
|
66
|
+
...FireworksTextPricing,
|
|
67
|
+
...FireworksEmbeddingPricing,
|
|
64
68
|
};
|
|
65
69
|
|
|
66
70
|
describe("Text completion models have pricing", () => {
|
|
@@ -166,6 +170,8 @@ describe("Model Catalog Coverage", () => {
|
|
|
166
170
|
...GeminiTextPricing,
|
|
167
171
|
...AnthropicTextPricing,
|
|
168
172
|
...XaiTextPricing,
|
|
173
|
+
...FireworksTextPricing,
|
|
174
|
+
...FireworksEmbeddingPricing,
|
|
169
175
|
} as Record<string, { deprecated?: boolean }>)
|
|
170
176
|
.filter(([, p]) => p && p.deprecated === true)
|
|
171
177
|
.map(([id]) => id)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { GenericAnthropicClient } from "../../src/clients/anthropic";
|
|
2
|
+
import { GenericGeminiClient } from "../../src/clients/gemini";
|
|
3
|
+
import { GenericOpenAiClient } from "../../src/clients/openai";
|
|
4
|
+
import { AnthropicModels } from "../../src/clients/pricing";
|
|
5
|
+
import {
|
|
6
|
+
REASONING_EFFORTS,
|
|
7
|
+
ReasoningEffort,
|
|
8
|
+
isReasoningEffort,
|
|
9
|
+
} from "../../src/clients/types";
|
|
10
|
+
import {
|
|
11
|
+
GoogleThinkingBudgetModels,
|
|
12
|
+
GoogleThinkingLevelModels,
|
|
13
|
+
} from "../../src/types";
|
|
14
|
+
import { GenericXAIClient } from "../../src/clients/xai";
|
|
15
|
+
|
|
16
|
+
const options = (model: string, reasoning_effort: ReasoningEffort) => ({
|
|
17
|
+
model,
|
|
18
|
+
reasoning_effort,
|
|
19
|
+
messages: [],
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("reasoning effort values", () => {
|
|
23
|
+
it("accepts the complete provider-neutral set and rejects other values", () => {
|
|
24
|
+
expect(REASONING_EFFORTS).toEqual([
|
|
25
|
+
"none",
|
|
26
|
+
"minimal",
|
|
27
|
+
"low",
|
|
28
|
+
"medium",
|
|
29
|
+
"high",
|
|
30
|
+
"xhigh",
|
|
31
|
+
"max",
|
|
32
|
+
]);
|
|
33
|
+
REASONING_EFFORTS.forEach((effort) => expect(isReasoningEffort(effort)).toBe(true));
|
|
34
|
+
expect(isReasoningEffort("extra-high")).toBe(false);
|
|
35
|
+
expect(isReasoningEffort(undefined)).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("maps provider-neutral values to OpenAI legacy and Responses spellings", () => {
|
|
39
|
+
const client = new GenericOpenAiClient("fake-key");
|
|
40
|
+
expect(client.resolveReasoningEffort(options("unknown", "none"))).toBe("low");
|
|
41
|
+
expect(client.resolveReasoningEffort(options("unknown", "minimal"))).toBe("low");
|
|
42
|
+
expect(client.resolveReasoningEffort(options("unknown", "xhigh"))).toBe("high");
|
|
43
|
+
expect(client.resolveReasoningEffort(options("unknown", "max"))).toBe("high");
|
|
44
|
+
expect(client.resolveReasoningEffortForModel(options("unknown", "max"))).toBe("xhigh");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("maps all values to valid Gemini thinking levels and budgets", () => {
|
|
48
|
+
const client = new GenericGeminiClient("fake-key");
|
|
49
|
+
const levelModel = GoogleThinkingLevelModels[0];
|
|
50
|
+
const budgetModel = GoogleThinkingBudgetModels[0];
|
|
51
|
+
|
|
52
|
+
expect(client.buildThinkingConfig(options(levelModel, "none"))).toEqual({ thinkingLevel: "minimal" });
|
|
53
|
+
expect(client.buildThinkingConfig(options(levelModel, "minimal"))).toEqual({ thinkingLevel: "minimal" });
|
|
54
|
+
expect(client.buildThinkingConfig(options(levelModel, "xhigh"))).toEqual({ thinkingLevel: "high" });
|
|
55
|
+
expect(client.buildThinkingConfig(options(levelModel, "max"))).toEqual({ thinkingLevel: "high" });
|
|
56
|
+
expect(client.buildThinkingConfig(options(budgetModel, "none"))).toEqual({ thinkingBudget: 0 });
|
|
57
|
+
expect(client.buildThinkingConfig(options(budgetModel, "minimal"))).toEqual({ thinkingBudget: 512 });
|
|
58
|
+
expect(client.buildThinkingConfig(options(budgetModel, "medium"))).toEqual({ thinkingBudget: 8192 });
|
|
59
|
+
expect(client.buildThinkingConfig(options(budgetModel, "max"))).toEqual({ thinkingBudget: -1 });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("maps provider-neutral values to Anthropic output effort", () => {
|
|
63
|
+
const client = new GenericAnthropicClient("fake-key");
|
|
64
|
+
expect(client.getEffortOutputConfig(AnthropicModels.Sonnet5, "none")).toBeUndefined();
|
|
65
|
+
expect(client.getEffortOutputConfig(AnthropicModels.Sonnet5, "minimal")).toEqual({ effort: "low" });
|
|
66
|
+
expect(client.getEffortOutputConfig(AnthropicModels.Sonnet5, "xhigh")).toEqual({ effort: "xhigh" });
|
|
67
|
+
expect(client.getEffortOutputConfig(AnthropicModels.Sonnet5, "max")).toEqual({ effort: "max" });
|
|
68
|
+
expect(client.getEffortOutputConfig("unsupported", "high")).toBeUndefined();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("maps provider-neutral values to xAI legacy effort", () => {
|
|
72
|
+
const client = new GenericXAIClient("fake-key");
|
|
73
|
+
expect(client.resolveLegacyReasoningEffort("none")).toBeUndefined();
|
|
74
|
+
expect(client.resolveLegacyReasoningEffort("minimal")).toBe("low");
|
|
75
|
+
expect(client.resolveLegacyReasoningEffort("medium")).toBe("medium");
|
|
76
|
+
expect(client.resolveLegacyReasoningEffort("xhigh")).toBe("high");
|
|
77
|
+
expect(client.resolveLegacyReasoningEffort("max")).toBe("high");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -111,7 +111,8 @@ describe("ToolResponseCache - MCP Format Support", () => {
|
|
|
111
111
|
it("should filter data array with ._data | map(select(.private == true))", async () => {
|
|
112
112
|
const result = await cache.queryToolResponse(
|
|
113
113
|
"github-repos",
|
|
114
|
-
"._data | map(select(.private == true))"
|
|
114
|
+
"._data | map(select(.private == true))",
|
|
115
|
+
{ maxCharacters: 50_000 }
|
|
115
116
|
);
|
|
116
117
|
|
|
117
118
|
// Parse and verify filtering worked
|