@ggui-ai/ui-gen 0.19.0 → 0.20.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.
@@ -62,7 +62,7 @@ interface AdapterResult {
62
62
  subagentsUsed?: string[];
63
63
  /** Tokens written to prompt cache (Claude-specific) */
64
64
  cacheCreationTokens?: number;
65
- /** Tokens read from prompt cache (Claude-specific) */
65
+ /** Tokens read from prompt cache (Claude / OpenRouter / OpenAI). */
66
66
  cacheReadTokens?: number;
67
67
  /** SDK session ID for evaluation loop resume (Claude SDK-specific) */
68
68
  sdkSessionId?: string;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/types.ts"],"names":[],"mappings":";AAcO,IAAM,sBAAA,GAAuD;AAAA,EAClE,MAAA,EAAQ,kBAAA;AAAA,EACR,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ,eAAA;AAAA,EACR,UAAA,EAAY;AACd","file":"types.js","sourcesContent":["// packages/ui-gen/src/adapters/types.ts\n//\n// Production adapter types — used by both Lambda generation and benchmarks.\n\nimport type { z } from 'zod';\nimport type { JsonObject } from '@ggui-ai/protocol';\n\n// =============================================================================\n// Provider & Mode\n// =============================================================================\n\nexport type ProviderName = 'claude' | 'openai' | 'google' | 'openrouter';\nexport type AdapterMode = 'raw' | 'sdk' | 'multi-agent';\n\nexport const PROVIDER_DISPLAY_NAMES: Record<ProviderName, string> = {\n claude: 'Anthropic Claude',\n openai: 'OpenAI',\n google: 'Google Gemini',\n openrouter: 'OpenRouter',\n};\n\n// =============================================================================\n// Tool Definition (SDK-agnostic)\n// =============================================================================\n\nexport interface ToolResultContent {\n type: 'text';\n text: string;\n}\n\nexport interface ToolResult {\n content: ToolResultContent[];\n isError?: boolean;\n}\n\n/**\n * SDK-agnostic tool definition.\n * Each adapter converts these to its native tool format.\n */\nexport interface ToolDefinition {\n /** Tool name (e.g., 'compile_component') */\n name: string;\n /** Tool description for the LLM */\n description: string;\n /** Zod schema for input parameters (always an object schema — tools take named args) */\n inputSchema: z.ZodObject<z.ZodRawShape>;\n /** Handler function — same signature regardless of SDK */\n handler: (args: JsonObject) => Promise<ToolResult>;\n}\n\n// =============================================================================\n// Adapter Result\n// =============================================================================\n\n/**\n * Result returned by any adapter's generate() method.\n *\n * Core fields are populated by all adapters. Extended fields are\n * populated by adapters that support richer output (e.g., Claude SDK).\n */\nexport interface AdapterResult {\n /** Final compiled JavaScript component code */\n compiledCode: string;\n /** TSX source code (captured from compile_component input) */\n sourceCode?: string;\n /** Token usage breakdown */\n tokens: {\n input: number;\n output: number;\n total: number;\n };\n /** Wall-clock generation time in ms */\n generationTimeMs: number;\n /** Total number of LLM API calls across all phases */\n turnsUsed: number;\n /** Number of harness iterations (Plan→Code→Evaluate→Regen cycles). 1 = single pass. */\n iterations?: number;\n /** Raw cost from SDK if available */\n rawCostUsd?: number;\n\n // ── Extended fields (production use) ───────────────────────────────\n\n /** Data spec for ggui_emit — describes what data the component accepts */\n stream?: JsonObject;\n /** LLM-generated metadata annotation for blueprint indexing */\n generatorMeta?: { category: string; description: string };\n /** Names of subagents invoked during generation */\n subagentsUsed?: string[];\n /** Tokens written to prompt cache (Claude-specific) */\n cacheCreationTokens?: number;\n /** Tokens read from prompt cache (Claude-specific) */\n cacheReadTokens?: number;\n /** SDK session ID for evaluation loop resume (Claude SDK-specific) */\n sdkSessionId?: string;\n}\n"]}
1
+ {"version":3,"sources":["../../src/adapters/types.ts"],"names":[],"mappings":";AAcO,IAAM,sBAAA,GAAuD;AAAA,EAClE,MAAA,EAAQ,kBAAA;AAAA,EACR,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ,eAAA;AAAA,EACR,UAAA,EAAY;AACd","file":"types.js","sourcesContent":["// packages/ui-gen/src/adapters/types.ts\n//\n// Production adapter types — used by both Lambda generation and benchmarks.\n\nimport type { z } from 'zod';\nimport type { JsonObject } from '@ggui-ai/protocol';\n\n// =============================================================================\n// Provider & Mode\n// =============================================================================\n\nexport type ProviderName = 'claude' | 'openai' | 'google' | 'openrouter';\nexport type AdapterMode = 'raw' | 'sdk' | 'multi-agent';\n\nexport const PROVIDER_DISPLAY_NAMES: Record<ProviderName, string> = {\n claude: 'Anthropic Claude',\n openai: 'OpenAI',\n google: 'Google Gemini',\n openrouter: 'OpenRouter',\n};\n\n// =============================================================================\n// Tool Definition (SDK-agnostic)\n// =============================================================================\n\nexport interface ToolResultContent {\n type: 'text';\n text: string;\n}\n\nexport interface ToolResult {\n content: ToolResultContent[];\n isError?: boolean;\n}\n\n/**\n * SDK-agnostic tool definition.\n * Each adapter converts these to its native tool format.\n */\nexport interface ToolDefinition {\n /** Tool name (e.g., 'compile_component') */\n name: string;\n /** Tool description for the LLM */\n description: string;\n /** Zod schema for input parameters (always an object schema — tools take named args) */\n inputSchema: z.ZodObject<z.ZodRawShape>;\n /** Handler function — same signature regardless of SDK */\n handler: (args: JsonObject) => Promise<ToolResult>;\n}\n\n// =============================================================================\n// Adapter Result\n// =============================================================================\n\n/**\n * Result returned by any adapter's generate() method.\n *\n * Core fields are populated by all adapters. Extended fields are\n * populated by adapters that support richer output (e.g., Claude SDK).\n */\nexport interface AdapterResult {\n /** Final compiled JavaScript component code */\n compiledCode: string;\n /** TSX source code (captured from compile_component input) */\n sourceCode?: string;\n /** Token usage breakdown */\n tokens: {\n input: number;\n output: number;\n total: number;\n };\n /** Wall-clock generation time in ms */\n generationTimeMs: number;\n /** Total number of LLM API calls across all phases */\n turnsUsed: number;\n /** Number of harness iterations (Plan→Code→Evaluate→Regen cycles). 1 = single pass. */\n iterations?: number;\n /** Raw cost from SDK if available */\n rawCostUsd?: number;\n\n // ── Extended fields (production use) ───────────────────────────────\n\n /** Data spec for ggui_emit — describes what data the component accepts */\n stream?: JsonObject;\n /** LLM-generated metadata annotation for blueprint indexing */\n generatorMeta?: { category: string; description: string };\n /** Names of subagents invoked during generation */\n subagentsUsed?: string[];\n /** Tokens written to prompt cache (Claude-specific) */\n cacheCreationTokens?: number;\n /** Tokens read from prompt cache (Claude / OpenRouter / OpenAI). */\n cacheReadTokens?: number;\n /** SDK session ID for evaluation loop resume (Claude SDK-specific) */\n sdkSessionId?: string;\n}\n"]}
@@ -22,6 +22,7 @@ import { createBrotliDecompress, createUnzip } from 'zlib';
22
22
  import { PlaywrightNotAvailableError, validateContractBehavior } from '@ggui-ai/ui-visual-tester';
23
23
  import { parseGeneratorSlug, formatGeneratorSlug, isValidGeneratorSlug } from '@ggui-ai/mcp-server-core';
24
24
  import { LUCIDE_ICON_NAMES, maxWidth } from '@ggui-ai/design';
25
+ import { primeInputs } from '@ggui-ai/ui-visual-tester/prime-inputs';
25
26
  import git from 'isomorphic-git';
26
27
  import { Volume, createFsFromVolume } from 'memfs';
27
28
  import { createPatch, applyPatch } from 'diff';
@@ -681,6 +682,27 @@ var init_sdk = __esm({
681
682
  }
682
683
  });
683
684
 
685
+ // src/adapters/openai/tokens.ts
686
+ function splitOpenAiUsage(inputTokensInclCached, outputTokens, cachedTokens) {
687
+ const cacheRead = Math.min(
688
+ Math.max(cachedTokens, 0),
689
+ Math.max(inputTokensInclCached, 0)
690
+ );
691
+ const nonCachedInput = Math.max(inputTokensInclCached, 0) - cacheRead;
692
+ return {
693
+ tokens: {
694
+ input: nonCachedInput,
695
+ output: outputTokens,
696
+ total: nonCachedInput + cacheRead + outputTokens
697
+ },
698
+ cacheReadTokens: cacheRead
699
+ };
700
+ }
701
+ var init_tokens = __esm({
702
+ "src/adapters/openai/tokens.ts"() {
703
+ }
704
+ });
705
+
684
706
  // src/adapters/openai/raw.ts
685
707
  var raw_exports2 = {};
686
708
  __export(raw_exports2, {
@@ -703,6 +725,7 @@ var init_raw2 = __esm({
703
725
  "src/adapters/openai/raw.ts"() {
704
726
  init_base();
705
727
  init_tool_bridge();
728
+ init_tokens();
706
729
  init_extract_code();
707
730
  OpenAiRawAdapter = class extends GeneratorAdapter {
708
731
  provider = "openai";
@@ -727,6 +750,7 @@ var init_raw2 = __esm({
727
750
  const capture = createCapture();
728
751
  let totalInputTokens = 0;
729
752
  let totalOutputTokens = 0;
753
+ let totalCachedInputTokens = 0;
730
754
  let turnsUsed = 0;
731
755
  let allTextOutput = "";
732
756
  let response = await this.client.responses.create({
@@ -742,6 +766,7 @@ var init_raw2 = __esm({
742
766
  if (response.usage) {
743
767
  totalInputTokens += response.usage.input_tokens ?? 0;
744
768
  totalOutputTokens += response.usage.output_tokens ?? 0;
769
+ totalCachedInputTokens += response.usage.input_tokens_details?.cached_tokens ?? 0;
745
770
  }
746
771
  const output = response.output ?? [];
747
772
  const functionCalls = output.filter(
@@ -807,11 +832,11 @@ var init_raw2 = __esm({
807
832
  sourceCode: capture.sourceCode,
808
833
  stream: capture.stream,
809
834
  generatorMeta: capture.generatorMeta,
810
- tokens: {
811
- input: totalInputTokens,
812
- output: totalOutputTokens,
813
- total: totalInputTokens + totalOutputTokens
814
- },
835
+ ...splitOpenAiUsage(
836
+ totalInputTokens,
837
+ totalOutputTokens,
838
+ totalCachedInputTokens
839
+ ),
815
840
  generationTimeMs: Date.now() - startTime,
816
841
  turnsUsed
817
842
  };
@@ -832,6 +857,7 @@ var OpenAiSdkAdapter;
832
857
  var init_sdk2 = __esm({
833
858
  "src/adapters/openai/sdk.ts"() {
834
859
  init_base();
860
+ init_tokens();
835
861
  init_extract_code();
836
862
  OpenAiSdkAdapter = class extends GeneratorAdapter {
837
863
  provider = "openai";
@@ -883,16 +909,16 @@ var init_sdk2 = __esm({
883
909
  const usage = result.state.usage;
884
910
  const inputTokens = usage.inputTokens;
885
911
  const outputTokens = usage.outputTokens;
912
+ const cachedInputTokens = (usage.inputTokensDetails ?? []).reduce(
913
+ (sum, details) => sum + (details.cached_tokens ?? 0),
914
+ 0
915
+ );
886
916
  return {
887
917
  compiledCode: capture.compiledCode,
888
918
  sourceCode: capture.sourceCode,
889
919
  stream: capture.stream,
890
920
  generatorMeta: capture.generatorMeta,
891
- tokens: {
892
- input: inputTokens,
893
- output: outputTokens,
894
- total: inputTokens + outputTokens
895
- },
921
+ ...splitOpenAiUsage(inputTokens, outputTokens, cachedInputTokens),
896
922
  generationTimeMs: Date.now() - startTime,
897
923
  turnsUsed: result.rawResponses?.length ?? 0
898
924
  };
@@ -13365,6 +13391,46 @@ function runRootWidthCap(input) {
13365
13391
  )
13366
13392
  ];
13367
13393
  }
13394
+ var LABEL_SPECIAL_CHAR = /[^a-z0-9\s]/i;
13395
+ var normText = (s) => s.toLowerCase().replace(/\s+/g, " ").trim();
13396
+ var alnumText = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
13397
+ function visibleText(sourceCode) {
13398
+ const jsx = (sourceCode.match(/>([^<>]*)</g) ?? []).map((m2) => m2.slice(1, -1).replace(/\{[^}]*\}/g, " ")).join(" ");
13399
+ let attrs = "";
13400
+ const attrRx = /\b(?:aria-label|placeholder|title|alt)\s*=\s*(?:"([^"]*)"|'([^']*)')/gi;
13401
+ let m;
13402
+ while ((m = attrRx.exec(sourceCode)) !== null) attrs += " " + (m[1] ?? m[2] ?? "");
13403
+ return jsx + " " + attrs;
13404
+ }
13405
+ function findDroppedActionLabels(sourceCode, contract) {
13406
+ const actionSpec = contract?.actionSpec;
13407
+ if (actionSpec === void 0) return [];
13408
+ if (/\.label\b/.test(sourceCode)) return [];
13409
+ const vis = visibleText(sourceCode);
13410
+ const normVis = normText(vis);
13411
+ const alnumVis = alnumText(vis);
13412
+ const out = [];
13413
+ for (const [name, entry] of Object.entries(actionSpec)) {
13414
+ const label = entry.label;
13415
+ if (typeof label !== "string" || label.length === 0 || label === name) continue;
13416
+ if (!LABEL_SPECIAL_CHAR.test(label)) continue;
13417
+ if (alnumText(label).length < 3) continue;
13418
+ if (normVis.includes(normText(label))) continue;
13419
+ if (alnumVis.includes(alnumText(label))) out.push({ name, label });
13420
+ }
13421
+ return out;
13422
+ }
13423
+ function runActionLabelDropped(input) {
13424
+ if (input.compiledCode === null) return [];
13425
+ return findDroppedActionLabels(input.sourceCode, input.contract).map(
13426
+ ({ name, label }) => mkIssue(
13427
+ "universal.action_label_dropped",
13428
+ `The action "${name}" declares label "${label}", but the rendered control appears to drop its "${label.replace(/[a-z0-9\s]/gi, "").trim()}": the label's words are in the generated source, the full label is not \u2014 a transcription drop (ggui#1190).`,
13429
+ `Render the label EXACTLY as the contract states it \u2014 button text should be the verbatim \`actionSpec.${name}.label\`, or render it from data (\`{x.label}\`), never a re-typed copy that drops "&", "/", "+", etc.`,
13430
+ "warn"
13431
+ )
13432
+ );
13433
+ }
13368
13434
  var UNIVERSAL_CHECKS = [
13369
13435
  {
13370
13436
  id: "universal.icon_name_known",
@@ -13390,6 +13456,12 @@ var UNIVERSAL_CHECKS = [
13390
13456
  values: ALL_RENDER_VALUES,
13391
13457
  run: runRootWidthCap
13392
13458
  },
13459
+ {
13460
+ id: "universal.action_label_dropped",
13461
+ axis: "render",
13462
+ values: ALL_RENDER_VALUES,
13463
+ run: runActionLabelDropped
13464
+ },
13393
13465
  {
13394
13466
  id: "universal.terminal_action_unguarded",
13395
13467
  axis: "render",
@@ -17619,8 +17691,6 @@ function editableSurface(el) {
17619
17691
  function parentLink(node) {
17620
17692
  return node;
17621
17693
  }
17622
-
17623
- // src/harness/check/runtime-render/render-check.ts
17624
17694
  async function runRenderCheck(input) {
17625
17695
  const g = hostGlobals();
17626
17696
  if ("window" in g && "document" in g) {
@@ -17799,6 +17869,10 @@ async function runRenderCheckInProcess(input) {
17799
17869
  let actionsChecked = 0;
17800
17870
  let streamsChecked = 0;
17801
17871
  try {
17872
+ try {
17873
+ primeInputs(renderResult.container);
17874
+ } catch {
17875
+ }
17802
17876
  if (input.contract?.actionSpec) {
17803
17877
  const actionSpec = input.contract.actionSpec;
17804
17878
  for (const [name, entry] of Object.entries(actionSpec)) {