@ggui-ai/ui-gen 0.9.0 → 0.10.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/dist/adapters/generation-dispatch.d.ts +9 -2
- package/dist/adapters/generation-dispatch.js +547 -85
- package/dist/adapters/generation-dispatch.js.map +1 -1
- package/dist/adapters/index.js +28 -11
- package/dist/adapters/index.js.map +1 -1
- package/dist/advanced/index.d.ts +5 -3
- package/dist/advanced/index.js +563 -92
- package/dist/advanced/index.js.map +1 -1
- package/dist/blueprint-validator.js +109 -3
- package/dist/blueprint-validator.js.map +1 -1
- package/dist/boilerplate.js +14 -2
- package/dist/boilerplate.js.map +1 -1
- package/dist/check/index.d.ts +1 -1
- package/dist/check/index.js +11 -1
- package/dist/check/index.js.map +1 -1
- package/dist/{create-ui-generator-B9kV4U9h.d.ts → create-ui-generator-CF0EL0Li.d.ts} +13 -1
- package/dist/{dispatch-Deo9qzrG.d.ts → dispatch-D75M-OfZ.d.ts} +1 -1
- package/dist/evaluation/axis-checks/index.d.ts +3 -3
- package/dist/evaluation/axis-checks/index.js +101 -0
- package/dist/evaluation/axis-checks/index.js.map +1 -1
- package/dist/evaluation/axis-checks/registry.d.ts +1 -1
- package/dist/evaluation/axis-checks/registry.js +101 -0
- package/dist/evaluation/axis-checks/registry.js.map +1 -1
- package/dist/evaluation/index.d.ts +46 -3
- package/dist/evaluation/index.js +358 -17
- package/dist/evaluation/index.js.map +1 -1
- package/dist/harness/check/runtime-render/index.d.ts +2 -2
- package/dist/harness/check/runtime-render/index.js +110 -3
- package/dist/harness/check/runtime-render/index.js.map +1 -1
- package/dist/harness/index.d.ts +2 -2
- package/dist/harness/index.js +263 -18
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/llm-trace-sink.d.ts +1 -1
- package/dist/harness/llm-trace-sink.js.map +1 -1
- package/dist/harness/prompts.js +17 -3
- package/dist/harness/prompts.js.map +1 -1
- package/dist/harness/result-types.d.ts +1 -1
- package/dist/harness/runtime.d.ts +9 -2
- package/dist/harness/runtime.js +20 -6
- package/dist/harness/runtime.js.map +1 -1
- package/dist/harness/types-public.d.ts +1 -1
- package/dist/harness/types-public.js +14 -2
- package/dist/harness/types-public.js.map +1 -1
- package/dist/harness/validator-trace-sink.d.ts +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +563 -92
- package/dist/index.js.map +1 -1
- package/dist/{llm-router-1TyI0_TV.d.ts → llm-router-BEVtbAb9.d.ts} +61 -4
- package/dist/patch.d.ts +1 -1
- package/dist/provider-adapter.d.ts +16 -6
- package/dist/provider-adapter.js.map +1 -1
- package/dist/providers/index.d.ts +19 -6
- package/dist/providers/index.js.map +1 -1
- package/dist/{render-check-CNZjHD5g.d.ts → render-check-BNYGVDJd.d.ts} +1 -1
- package/dist/{types-public-C36k1q6s.d.ts → types-public-DIpjC-OA.d.ts} +1 -1
- package/dist/workflows.d.ts +1 -1
- package/package.json +12 -12
- package/src/adapters/generation-dispatch.ts +9 -0
- package/src/adapters/openrouter/client.ts +2 -2
- package/src/adapters/openrouter/types.ts +10 -0
- package/src/boilerplate/generate.ts +10 -2
- package/src/boilerplate/json-schema-ts.ts +22 -2
- package/src/boilerplate/system-prompt.ts +1 -0
- package/src/compile.ts +1 -1
- package/src/contract-context.ts +10 -2
- package/src/create-ui-generator.ts +14 -0
- package/src/design-system-docs.ts +6 -4
- package/src/evaluation/axis-checks/checks/state-ui-affordance.ts +162 -0
- package/src/evaluation/axis-checks/registry.ts +2 -0
- package/src/evaluation/index.ts +11 -0
- package/src/evaluation/llm-evaluator.ts +14 -1
- package/src/evaluation/visual-evaluator.ts +44 -65
- package/src/harness/check/runtime-render/adapter.ts +18 -0
- package/src/harness/check/runtime-render/index.ts +8 -0
- package/src/harness/check/runtime-render/prepare-mockup.ts +14 -0
- package/src/harness/check/runtime-render/production-act-shim.ts +158 -0
- package/src/harness/check/runtime-render/render-check.ts +72 -1
- package/src/harness/coding/run-eval-round.ts +61 -5
- package/src/harness/index.ts +1 -1
- package/src/harness/llm-router.ts +397 -18
- package/src/harness/llm-trace-sink.ts +2 -1
- package/src/harness/runtime.ts +7 -0
- package/src/index.ts +5 -5
- package/src/provider-adapter.ts +16 -6
- package/src/providers/bedrock.ts +5 -4
- package/src/providers/index.ts +16 -4
- package/src/tools/render-check.ts +19 -7
package/dist/harness/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Harness } from './types-public.js';
|
|
2
2
|
export { CheckLeg, CheckResult, CreateHarnessInput, DefaultTaskRunner, HarnessConstructionContext, HarnessId, HarnessMeta, HarnessName, HarnessOverrides, HarnessRevision, HowLeg, IterationRecord, LLMEvaluator, PatchFn, Phase, PhaseRunResult, PlannerDecision, PlannerFn, ProcessLeg, PromptBuilder, RetryPolicy, RunCheckInput, RunHarnessInput, RunHarnessReason, RunHarnessResult, RunWorkflowInput, Task, TaskContext, TaskRunner, TierCheck, WhatLeg, Workflow, WorkflowRunResult, createHarness, runCheck, runHarness, runWorkflow } from './types-public.js';
|
|
3
3
|
export { applyLineRanges, defaultApplyPatch } from '../patch.js';
|
|
4
|
-
export { A as AgentConfig, L as LLMResponse, c as callLLM, a as createAgent } from '../llm-router-
|
|
4
|
+
export { A as AgentConfig, L as LLMResponse, P as ProviderRetryInfo, c as callLLM, a as createAgent } from '../llm-router-BEVtbAb9.js';
|
|
5
5
|
export { WORKFLOWS, WorkflowId, pickWorkflow } from '../workflows.js';
|
|
6
6
|
export { LlmTraceEvent, LlmTraceKind, LlmTraceProvider, LlmTraceSink, getLlmTraceSink, setLlmTraceSink } from './llm-trace-sink.js';
|
|
7
7
|
export { ValidatorTraceEvent, ValidatorTraceSink, getValidatorTraceSink, setValidatorTraceSink } from './validator-trace-sink.js';
|
|
@@ -9,7 +9,7 @@ export { ProcessMode } from '../policy.js';
|
|
|
9
9
|
export { computeHarnessId, computeHarnessName, hashClassification } from '../hash.js';
|
|
10
10
|
import '@ggui-ai/protocol';
|
|
11
11
|
import '../axes-CzLEMDeB.js';
|
|
12
|
-
import '../types-public-
|
|
12
|
+
import '../types-public-DIpjC-OA.js';
|
|
13
13
|
import '../types-BOvHNG7K.js';
|
|
14
14
|
import '../llm.js';
|
|
15
15
|
|
package/dist/harness/index.js
CHANGED
|
@@ -27,14 +27,16 @@ var OpenRouterError;
|
|
|
27
27
|
var init_types = __esm({
|
|
28
28
|
"src/adapters/openrouter/types.ts"() {
|
|
29
29
|
OpenRouterError = class extends Error {
|
|
30
|
-
constructor(message, status, code) {
|
|
30
|
+
constructor(message, status, code, headers) {
|
|
31
31
|
super(message);
|
|
32
32
|
this.status = status;
|
|
33
33
|
this.code = code;
|
|
34
|
+
this.headers = headers;
|
|
34
35
|
this.name = "OpenRouterError";
|
|
35
36
|
}
|
|
36
37
|
status;
|
|
37
38
|
code;
|
|
39
|
+
headers;
|
|
38
40
|
get isTransient() {
|
|
39
41
|
return this.status === 408 || this.status === 429 || this.status === 502 || this.status === 503;
|
|
40
42
|
}
|
|
@@ -79,7 +81,7 @@ var init_client = __esm({
|
|
|
79
81
|
if (!response.ok) {
|
|
80
82
|
const errorBody = await response.json().catch(() => ({ error: { message: response.statusText } }));
|
|
81
83
|
const message = errorBody.error?.message ?? response.statusText;
|
|
82
|
-
throw new OpenRouterError(message, response.status);
|
|
84
|
+
throw new OpenRouterError(message, response.status, void 0, response.headers);
|
|
83
85
|
}
|
|
84
86
|
return response.json();
|
|
85
87
|
}
|
|
@@ -97,7 +99,7 @@ var init_client = __esm({
|
|
|
97
99
|
if (!response.ok) {
|
|
98
100
|
const errorBody = await response.json().catch(() => ({ error: { message: response.statusText } }));
|
|
99
101
|
const message = errorBody.error?.message ?? response.statusText;
|
|
100
|
-
throw new OpenRouterError(message, response.status);
|
|
102
|
+
throw new OpenRouterError(message, response.status, void 0, response.headers);
|
|
101
103
|
}
|
|
102
104
|
if (!response.body) {
|
|
103
105
|
throw new OpenRouterError("No response body for streaming request", 500);
|
|
@@ -883,6 +885,15 @@ function jsonSchemaTypeToTs(schema) {
|
|
|
883
885
|
if (schema.enum?.length) {
|
|
884
886
|
return schema.enum.map((v) => typeof v === "string" ? `'${v}'` : String(v)).join(" | ");
|
|
885
887
|
}
|
|
888
|
+
if (Array.isArray(schema.type)) {
|
|
889
|
+
const { nullable: _nullable, ...rest } = schema;
|
|
890
|
+
const members = [...new Set(schema.type)].map(
|
|
891
|
+
(member) => jsonSchemaTypeToTs({ ...rest, type: member })
|
|
892
|
+
);
|
|
893
|
+
const unique = [...new Set(members)];
|
|
894
|
+
if (schema.nullable && !unique.includes("null")) unique.push("null");
|
|
895
|
+
return unique.join(" | ");
|
|
896
|
+
}
|
|
886
897
|
let result;
|
|
887
898
|
switch (schema.type) {
|
|
888
899
|
case "string":
|
|
@@ -900,7 +911,8 @@ function jsonSchemaTypeToTs(schema) {
|
|
|
900
911
|
case "array": {
|
|
901
912
|
if (schema.items) {
|
|
902
913
|
const itemType = jsonSchemaTypeToTs(schema.items);
|
|
903
|
-
|
|
914
|
+
const itemsDeclareObject = Array.isArray(schema.items.type) ? schema.items.type.includes("object") : schema.items.type === "object";
|
|
915
|
+
result = itemsDeclareObject && schema.items.properties ? `Array<${itemType}>` : itemType.includes("|") ? `(${itemType})[]` : `${itemType}[]`;
|
|
904
916
|
} else {
|
|
905
917
|
result = "unknown[]";
|
|
906
918
|
}
|
|
@@ -1033,13 +1045,14 @@ function generateBoilerplate(_userPrompt, contract, shellType, screen, composedS
|
|
|
1033
1045
|
const propsFields = [];
|
|
1034
1046
|
const propsProperties = contract?.propsSpec?.properties ?? {};
|
|
1035
1047
|
for (const [key, entry] of Object.entries(propsProperties)) {
|
|
1036
|
-
const required = entry.required
|
|
1048
|
+
const required = entry.required === true;
|
|
1037
1049
|
const nullable = entry.schema.nullable === true;
|
|
1038
1050
|
const tsType = jsonSchemaTypeToTs(entry.schema);
|
|
1039
1051
|
const fullType = nullable ? `${tsType} | null` : tsType;
|
|
1040
1052
|
const parts = [];
|
|
1041
1053
|
if (entry.description) parts.push(entry.description);
|
|
1042
1054
|
if (entry.default !== void 0) parts.push(`(default: ${JSON.stringify(entry.default)})`);
|
|
1055
|
+
if (!required) parts.push("(optional \u2014 the agent may omit it; guard or fall back)");
|
|
1043
1056
|
const desc = parts.length > 0 ? ` // ${parts.join(" ")}` : "";
|
|
1044
1057
|
propsFields.push(` ${key}${required ? "" : "?"}: ${fullType};${desc}`);
|
|
1045
1058
|
}
|
|
@@ -1787,6 +1800,7 @@ A \`DataContract\` declares everything a GguiSession exchanges with the outside
|
|
|
1787
1800
|
|
|
1788
1801
|
Props arrive at mount and may be partial on first render; later prop changes reach the SAME mounted component via \`ggui_amend\` (in-place repaint) on the live channel. Stream channels start empty and fill over time. Context slots start at their declared default (often \`null\`). **Never assume a field exists before you read it.**
|
|
1789
1802
|
|
|
1803
|
+
- **Optional contract props**: a \`Props\` field marked \`?\` in the boilerplate may be omitted by the agent ENTIRELY \u2014 the wire gate only enforces \`required: true\` entries, and the harness verifies your component with those props stripped. Guard them (\`?? default\`, \`?.\`, or an empty-state branch) from the FIRST render.
|
|
1790
1804
|
- **Array iteration**: always default to \`[]\` before \`.map\`/\`.filter\`/\`.length\`. Use \`(props.items ?? []).map(...)\` not \`props.items.map(...)\`. Same for stream.all, stream.latest, etc.
|
|
1791
1805
|
- **Object access**: optional-chain through nested fields. \`props.user?.name ?? 'Anonymous'\` not \`props.user.name\`.
|
|
1792
1806
|
- **Number ops**: default before arithmetic. \`(props.count ?? 0) + 1\` not \`props.count + 1\`.
|
|
@@ -2816,6 +2830,34 @@ function errorSummary(e) {
|
|
|
2816
2830
|
}
|
|
2817
2831
|
return `${e.constructor.name}${status}${code}: ${e.message.slice(0, 120)}${body}`;
|
|
2818
2832
|
}
|
|
2833
|
+
function isRateLimited(e) {
|
|
2834
|
+
if (!(e instanceof Error) || !("status" in e)) return false;
|
|
2835
|
+
return e.status === 429;
|
|
2836
|
+
}
|
|
2837
|
+
function extractStatus(e) {
|
|
2838
|
+
if (!(e instanceof Error) || !("status" in e)) return void 0;
|
|
2839
|
+
const status = e.status;
|
|
2840
|
+
return typeof status === "number" ? status : void 0;
|
|
2841
|
+
}
|
|
2842
|
+
function extractRetryAfterSec(e) {
|
|
2843
|
+
if (!(e instanceof Error) || !("headers" in e)) return void 0;
|
|
2844
|
+
const headers = e.headers;
|
|
2845
|
+
if (headers === null || typeof headers !== "object" || !("get" in headers) || typeof headers.get !== "function") {
|
|
2846
|
+
return void 0;
|
|
2847
|
+
}
|
|
2848
|
+
const raw = headers.get("retry-after");
|
|
2849
|
+
if (raw === null) return void 0;
|
|
2850
|
+
const parsed = Number(raw);
|
|
2851
|
+
return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : void 0;
|
|
2852
|
+
}
|
|
2853
|
+
function sleep(ms) {
|
|
2854
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2855
|
+
}
|
|
2856
|
+
var MAX_RETRY_ATTEMPTS = 2;
|
|
2857
|
+
var RETRY_AFTER_CAP_SEC = 15;
|
|
2858
|
+
var DEFAULT_BACKOFF_BASE_MS = 1e3;
|
|
2859
|
+
var DEFAULT_BACKOFF_MAX_MS = 8e3;
|
|
2860
|
+
var MAX_TOTAL_RETRY_DELAY_MS = 2e4;
|
|
2819
2861
|
var LLMAgent = class {
|
|
2820
2862
|
client = null;
|
|
2821
2863
|
// ── Session state for server-side chaining ──────────────
|
|
@@ -2830,8 +2872,11 @@ var LLMAgent = class {
|
|
|
2830
2872
|
* back to `process.env` exactly as before.
|
|
2831
2873
|
*/
|
|
2832
2874
|
routeOverride;
|
|
2833
|
-
|
|
2875
|
+
/** See `AgentConfig.onRetry` (#489). Absent is a no-op. */
|
|
2876
|
+
onRetry;
|
|
2877
|
+
constructor(routeOverride, onRetry) {
|
|
2834
2878
|
this.routeOverride = routeOverride;
|
|
2879
|
+
this.onRetry = onRetry;
|
|
2835
2880
|
}
|
|
2836
2881
|
async getClient() {
|
|
2837
2882
|
if (!this.client) {
|
|
@@ -2866,17 +2911,83 @@ var LLMAgent = class {
|
|
|
2866
2911
|
async sendToolResult(_results) {
|
|
2867
2912
|
}
|
|
2868
2913
|
/**
|
|
2869
|
-
* Execute an API call
|
|
2870
|
-
*
|
|
2914
|
+
* Execute an API call, retrying once or twice on HTTP 429
|
|
2915
|
+
* (rate-limited) before giving up (#489).
|
|
2916
|
+
*
|
|
2917
|
+
* Policy: on a 429, honor the provider's `Retry-After` header when
|
|
2918
|
+
* present (capped at `RETRY_AFTER_CAP_SEC` — a provider asking for a
|
|
2919
|
+
* longer wait is treated as "don't retry", not "wait longer": making
|
|
2920
|
+
* the caller sit through 15+ more seconds on a single failed call is
|
|
2921
|
+
* a bad experience regardless of what's driving the request);
|
|
2922
|
+
* otherwise fall back to exponential backoff with jitter, capped at
|
|
2923
|
+
* `DEFAULT_BACKOFF_MAX_MS` per attempt. Stops after
|
|
2924
|
+
* `MAX_RETRY_ATTEMPTS` retries OR once the cumulative delay would
|
|
2925
|
+
* exceed `MAX_TOTAL_RETRY_DELAY_MS`, whichever comes first. Any
|
|
2926
|
+
* non-429 error, or a 429 with no attempts left, is logged and
|
|
2927
|
+
* re-thrown immediately — unchanged from the pre-#489 behavior.
|
|
2928
|
+
*
|
|
2929
|
+
* **Budget scope (final-review correction):** `MAX_TOTAL_RETRY_DELAY_MS`
|
|
2930
|
+
* bounds a single `apiCall()` invocation, not a whole generation. A
|
|
2931
|
+
* generation issues many sequential `apiCall()` calls (coding turns,
|
|
2932
|
+
* tool-result round-trips, eval rounds); under sustained throttling
|
|
2933
|
+
* each one can independently add up to `MAX_TOTAL_RETRY_DELAY_MS`, so
|
|
2934
|
+
* a generation with k rate-limited calls can add up to roughly `k *
|
|
2935
|
+
* MAX_TOTAL_RETRY_DELAY_MS` to how long it holds its caller's
|
|
2936
|
+
* resources (e.g. the caller's concurrency slot) — not a flat 20s
|
|
2937
|
+
* ceiling. No shared per-generation budget is threaded here by
|
|
2938
|
+
* design (accepted ruling, not an oversight): the caller's own
|
|
2939
|
+
* queue/admission backstop is what bounds how long other
|
|
2940
|
+
* queued work waits, independent of any one generation's retry
|
|
2941
|
+
* total.
|
|
2871
2942
|
*/
|
|
2872
2943
|
async apiCall(fn) {
|
|
2873
2944
|
const start = Date.now();
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2945
|
+
let totalDelayMs = 0;
|
|
2946
|
+
for (let attempt = 0; ; attempt++) {
|
|
2947
|
+
try {
|
|
2948
|
+
return await fn();
|
|
2949
|
+
} catch (e) {
|
|
2950
|
+
const ms = Date.now() - start;
|
|
2951
|
+
if (attempt >= MAX_RETRY_ATTEMPTS || !isRateLimited(e)) {
|
|
2952
|
+
console.error(`[${this.provider}] API error after ${ms}ms: ${errorSummary(e)}`);
|
|
2953
|
+
throw e;
|
|
2954
|
+
}
|
|
2955
|
+
const retryAfterSec = extractRetryAfterSec(e);
|
|
2956
|
+
if (retryAfterSec !== void 0 && retryAfterSec > RETRY_AFTER_CAP_SEC) {
|
|
2957
|
+
console.error(
|
|
2958
|
+
`[${this.provider}] API error after ${ms}ms (retry-after ${retryAfterSec}s exceeds ${RETRY_AFTER_CAP_SEC}s cap, not retrying): ${errorSummary(e)}`
|
|
2959
|
+
);
|
|
2960
|
+
throw e;
|
|
2961
|
+
}
|
|
2962
|
+
const delayMs = retryAfterSec !== void 0 ? retryAfterSec * 1e3 : Math.min(DEFAULT_BACKOFF_BASE_MS * 2 ** attempt, DEFAULT_BACKOFF_MAX_MS) + Math.floor(Math.random() * 250);
|
|
2963
|
+
if (totalDelayMs + delayMs > MAX_TOTAL_RETRY_DELAY_MS) {
|
|
2964
|
+
console.error(
|
|
2965
|
+
`[${this.provider}] API error after ${ms}ms (retry budget exhausted, not retrying): ${errorSummary(e)}`
|
|
2966
|
+
);
|
|
2967
|
+
throw e;
|
|
2968
|
+
}
|
|
2969
|
+
totalDelayMs += delayMs;
|
|
2970
|
+
const info = {
|
|
2971
|
+
provider: this.provider,
|
|
2972
|
+
attempt: attempt + 1,
|
|
2973
|
+
maxAttempts: MAX_RETRY_ATTEMPTS,
|
|
2974
|
+
status: extractStatus(e),
|
|
2975
|
+
retryAfterSec,
|
|
2976
|
+
delayMs,
|
|
2977
|
+
message: errorSummary(e)
|
|
2978
|
+
};
|
|
2979
|
+
console.warn(
|
|
2980
|
+
`[${this.provider}] rate-limited (attempt ${info.attempt}/${info.maxAttempts}), retrying in ${delayMs}ms`
|
|
2981
|
+
);
|
|
2982
|
+
try {
|
|
2983
|
+
this.onRetry?.(info);
|
|
2984
|
+
} catch (observerError) {
|
|
2985
|
+
console.warn(
|
|
2986
|
+
`[${this.provider}] onRetry observer threw (ignored, retry proceeds): ${errorSummary(observerError)}`
|
|
2987
|
+
);
|
|
2988
|
+
}
|
|
2989
|
+
await sleep(delayMs);
|
|
2990
|
+
}
|
|
2880
2991
|
}
|
|
2881
2992
|
}
|
|
2882
2993
|
};
|
|
@@ -2969,6 +3080,73 @@ var AnthropicAgent = class extends LLMAgent {
|
|
|
2969
3080
|
throw e;
|
|
2970
3081
|
}
|
|
2971
3082
|
}
|
|
3083
|
+
async callVision(model, systemPrompt, userPrompt, image, maxTokens) {
|
|
3084
|
+
const client = await this.getClient();
|
|
3085
|
+
const traceId = newLlmTraceId();
|
|
3086
|
+
const startedAt = Date.now();
|
|
3087
|
+
const resolvedModel = this.resolveModel(model);
|
|
3088
|
+
const imageBlock = {
|
|
3089
|
+
type: "image",
|
|
3090
|
+
source: {
|
|
3091
|
+
type: "base64",
|
|
3092
|
+
media_type: image.mediaType,
|
|
3093
|
+
data: image.base64
|
|
3094
|
+
}
|
|
3095
|
+
};
|
|
3096
|
+
try {
|
|
3097
|
+
const response = await this.apiCall(
|
|
3098
|
+
() => client.messages.create({
|
|
3099
|
+
model: resolvedModel,
|
|
3100
|
+
max_tokens: maxTokens ?? 1500,
|
|
3101
|
+
system: systemPrompt,
|
|
3102
|
+
messages: [
|
|
3103
|
+
{
|
|
3104
|
+
role: "user",
|
|
3105
|
+
content: [imageBlock, { type: "text", text: userPrompt }]
|
|
3106
|
+
}
|
|
3107
|
+
]
|
|
3108
|
+
})
|
|
3109
|
+
);
|
|
3110
|
+
const text = response.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
3111
|
+
const endedAt = Date.now();
|
|
3112
|
+
emitLlmTraceEvent({
|
|
3113
|
+
id: traceId,
|
|
3114
|
+
at: startedAt,
|
|
3115
|
+
endedAt,
|
|
3116
|
+
durationMs: endedAt - startedAt,
|
|
3117
|
+
provider: "anthropic",
|
|
3118
|
+
model: resolvedModel,
|
|
3119
|
+
kind: "callVision",
|
|
3120
|
+
systemPrompt,
|
|
3121
|
+
userPrompt,
|
|
3122
|
+
result: {
|
|
3123
|
+
inputTokens: response.usage.input_tokens,
|
|
3124
|
+
outputTokens: response.usage.output_tokens,
|
|
3125
|
+
text
|
|
3126
|
+
}
|
|
3127
|
+
});
|
|
3128
|
+
return {
|
|
3129
|
+
text,
|
|
3130
|
+
inputTokens: response.usage.input_tokens,
|
|
3131
|
+
outputTokens: response.usage.output_tokens
|
|
3132
|
+
};
|
|
3133
|
+
} catch (e) {
|
|
3134
|
+
const endedAt = Date.now();
|
|
3135
|
+
emitLlmTraceEvent({
|
|
3136
|
+
id: traceId,
|
|
3137
|
+
at: startedAt,
|
|
3138
|
+
endedAt,
|
|
3139
|
+
durationMs: endedAt - startedAt,
|
|
3140
|
+
provider: "anthropic",
|
|
3141
|
+
model: resolvedModel,
|
|
3142
|
+
kind: "callVision",
|
|
3143
|
+
systemPrompt,
|
|
3144
|
+
userPrompt,
|
|
3145
|
+
error: { message: e instanceof Error ? e.message : String(e) }
|
|
3146
|
+
});
|
|
3147
|
+
throw e;
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
2972
3150
|
async callTools(model, systemPrompt, userPrompt, tools, toolChoice = "required", _scopedTools) {
|
|
2973
3151
|
const client = await this.getClient();
|
|
2974
3152
|
const traceId = newLlmTraceId();
|
|
@@ -3364,6 +3542,72 @@ var GoogleAgent = class extends LLMAgent {
|
|
|
3364
3542
|
outputTokens: interaction.usage?.total_output_tokens ?? 0
|
|
3365
3543
|
};
|
|
3366
3544
|
}
|
|
3545
|
+
async callVision(model, systemPrompt, userPrompt, image, maxTokens) {
|
|
3546
|
+
const client = await this.getClient();
|
|
3547
|
+
const traceId = newLlmTraceId();
|
|
3548
|
+
const startedAt = Date.now();
|
|
3549
|
+
const resolvedModel = this.resolveModel(model);
|
|
3550
|
+
try {
|
|
3551
|
+
const response = await this.apiCall(
|
|
3552
|
+
() => client.models.generateContent({
|
|
3553
|
+
model: resolvedModel,
|
|
3554
|
+
contents: [
|
|
3555
|
+
{
|
|
3556
|
+
role: "user",
|
|
3557
|
+
parts: [
|
|
3558
|
+
{ inlineData: { mimeType: image.mediaType, data: image.base64 } },
|
|
3559
|
+
{ text: userPrompt }
|
|
3560
|
+
]
|
|
3561
|
+
}
|
|
3562
|
+
],
|
|
3563
|
+
config: {
|
|
3564
|
+
systemInstruction: systemPrompt,
|
|
3565
|
+
...maxTokens ? { maxOutputTokens: maxTokens } : {}
|
|
3566
|
+
}
|
|
3567
|
+
})
|
|
3568
|
+
);
|
|
3569
|
+
const text = response.candidates?.[0]?.content?.parts?.map((p) => p.text || "").join("") || "";
|
|
3570
|
+
const usage = response.usageMetadata;
|
|
3571
|
+
const result = {
|
|
3572
|
+
text,
|
|
3573
|
+
inputTokens: usage?.promptTokenCount ?? 0,
|
|
3574
|
+
outputTokens: usage?.candidatesTokenCount ?? 0
|
|
3575
|
+
};
|
|
3576
|
+
const endedAt = Date.now();
|
|
3577
|
+
emitLlmTraceEvent({
|
|
3578
|
+
id: traceId,
|
|
3579
|
+
at: startedAt,
|
|
3580
|
+
endedAt,
|
|
3581
|
+
durationMs: endedAt - startedAt,
|
|
3582
|
+
provider: "google",
|
|
3583
|
+
model: resolvedModel,
|
|
3584
|
+
kind: "callVision",
|
|
3585
|
+
systemPrompt,
|
|
3586
|
+
userPrompt,
|
|
3587
|
+
result: {
|
|
3588
|
+
inputTokens: result.inputTokens,
|
|
3589
|
+
outputTokens: result.outputTokens,
|
|
3590
|
+
text
|
|
3591
|
+
}
|
|
3592
|
+
});
|
|
3593
|
+
return result;
|
|
3594
|
+
} catch (e) {
|
|
3595
|
+
const endedAt = Date.now();
|
|
3596
|
+
emitLlmTraceEvent({
|
|
3597
|
+
id: traceId,
|
|
3598
|
+
at: startedAt,
|
|
3599
|
+
endedAt,
|
|
3600
|
+
durationMs: endedAt - startedAt,
|
|
3601
|
+
provider: "google",
|
|
3602
|
+
model: resolvedModel,
|
|
3603
|
+
kind: "callVision",
|
|
3604
|
+
systemPrompt,
|
|
3605
|
+
userPrompt,
|
|
3606
|
+
error: { message: e instanceof Error ? e.message : String(e) }
|
|
3607
|
+
});
|
|
3608
|
+
throw e;
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3367
3611
|
async callTools(model, systemPrompt, userPrompt, tools, _toolChoice = "required", scopedTools) {
|
|
3368
3612
|
const client = await this.getClient();
|
|
3369
3613
|
const interactionTools = tools.map((t) => ({
|
|
@@ -3695,15 +3939,16 @@ var OpenRouterAgent = class extends LLMAgent {
|
|
|
3695
3939
|
function createAgent(providerOrConfig) {
|
|
3696
3940
|
const provider = typeof providerOrConfig === "string" ? providerOrConfig : providerOrConfig.provider;
|
|
3697
3941
|
const routeOverride = typeof providerOrConfig === "string" ? void 0 : providerOrConfig.routeOverride;
|
|
3942
|
+
const onRetry = typeof providerOrConfig === "string" ? void 0 : providerOrConfig.onRetry;
|
|
3698
3943
|
switch (provider) {
|
|
3699
3944
|
case "anthropic":
|
|
3700
|
-
return new AnthropicAgent(routeOverride);
|
|
3945
|
+
return new AnthropicAgent(routeOverride, onRetry);
|
|
3701
3946
|
case "openai":
|
|
3702
|
-
return new OpenAIAgent(routeOverride);
|
|
3947
|
+
return new OpenAIAgent(routeOverride, onRetry);
|
|
3703
3948
|
case "google":
|
|
3704
|
-
return new GoogleAgent(routeOverride);
|
|
3949
|
+
return new GoogleAgent(routeOverride, onRetry);
|
|
3705
3950
|
case "openrouter":
|
|
3706
|
-
return new OpenRouterAgent(routeOverride);
|
|
3951
|
+
return new OpenRouterAgent(routeOverride, onRetry);
|
|
3707
3952
|
}
|
|
3708
3953
|
}
|
|
3709
3954
|
async function callLLM(config, systemPrompt, userPrompt, maxTokens) {
|