hazo_llm_api 1.2.13 → 1.3.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.
Files changed (204) hide show
  1. package/README.md +321 -8
  2. package/config/hazo_llm_api_config.ini +33 -0
  3. package/dist/components/index.d.ts +2 -0
  4. package/dist/components/index.d.ts.map +1 -1
  5. package/dist/components/index.js +2 -0
  6. package/dist/components/index.js.map +1 -1
  7. package/dist/components/llm_call_inspector/index.d.ts +6 -0
  8. package/dist/components/llm_call_inspector/index.d.ts.map +1 -0
  9. package/dist/components/llm_call_inspector/index.js +5 -0
  10. package/dist/components/llm_call_inspector/index.js.map +1 -0
  11. package/dist/components/llm_call_inspector/llm_call_inspector.d.ts +18 -0
  12. package/dist/components/llm_call_inspector/llm_call_inspector.d.ts.map +1 -0
  13. package/dist/components/llm_call_inspector/llm_call_inspector.js +103 -0
  14. package/dist/components/llm_call_inspector/llm_call_inspector.js.map +1 -0
  15. package/dist/components/llm_cost_dashboard/index.d.ts +6 -0
  16. package/dist/components/llm_cost_dashboard/index.d.ts.map +1 -0
  17. package/dist/components/llm_cost_dashboard/index.js +5 -0
  18. package/dist/components/llm_cost_dashboard/index.js.map +1 -0
  19. package/dist/components/llm_cost_dashboard/llm_cost_dashboard.d.ts +16 -0
  20. package/dist/components/llm_cost_dashboard/llm_cost_dashboard.d.ts.map +1 -0
  21. package/dist/components/llm_cost_dashboard/llm_cost_dashboard.js +154 -0
  22. package/dist/components/llm_cost_dashboard/llm_cost_dashboard.js.map +1 -0
  23. package/dist/index.d.ts +2 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/lib/cascade/cascade_runner.d.ts +50 -0
  26. package/dist/lib/cascade/cascade_runner.d.ts.map +1 -0
  27. package/dist/lib/cascade/cascade_runner.js +115 -0
  28. package/dist/lib/cascade/cascade_runner.js.map +1 -0
  29. package/dist/lib/cascade/index.d.ts +5 -0
  30. package/dist/lib/cascade/index.d.ts.map +1 -0
  31. package/dist/lib/cascade/index.js +4 -0
  32. package/dist/lib/cascade/index.js.map +1 -0
  33. package/dist/lib/cascade/types.d.ts +35 -0
  34. package/dist/lib/cascade/types.d.ts.map +1 -0
  35. package/dist/lib/cascade/types.js +14 -0
  36. package/dist/lib/cascade/types.js.map +1 -0
  37. package/dist/lib/cost_cap/cost_cap.d.ts +40 -0
  38. package/dist/lib/cost_cap/cost_cap.d.ts.map +1 -0
  39. package/dist/lib/cost_cap/cost_cap.js +150 -0
  40. package/dist/lib/cost_cap/cost_cap.js.map +1 -0
  41. package/dist/lib/cost_cap/index.d.ts +3 -0
  42. package/dist/lib/cost_cap/index.d.ts.map +1 -0
  43. package/dist/lib/cost_cap/index.js +2 -0
  44. package/dist/lib/cost_cap/index.js.map +1 -0
  45. package/dist/lib/database/init_api_log.d.ts +10 -0
  46. package/dist/lib/database/init_api_log.d.ts.map +1 -0
  47. package/dist/lib/database/init_api_log.js +91 -0
  48. package/dist/lib/database/init_api_log.js.map +1 -0
  49. package/dist/lib/hazo_connect/direct_db_connect.d.ts +11 -7
  50. package/dist/lib/hazo_connect/direct_db_connect.d.ts.map +1 -1
  51. package/dist/lib/hazo_connect/direct_db_connect.js +59 -6
  52. package/dist/lib/hazo_connect/direct_db_connect.js.map +1 -1
  53. package/dist/lib/hazo_connect/types.d.ts +35 -3
  54. package/dist/lib/hazo_connect/types.d.ts.map +1 -1
  55. package/dist/lib/llm_api/embed_cache.d.ts +15 -0
  56. package/dist/lib/llm_api/embed_cache.d.ts.map +1 -0
  57. package/dist/lib/llm_api/embed_cache.js +53 -0
  58. package/dist/lib/llm_api/embed_cache.js.map +1 -0
  59. package/dist/lib/llm_api/hazo_llm_document_text.d.ts.map +1 -1
  60. package/dist/lib/llm_api/hazo_llm_document_text.js +56 -14
  61. package/dist/lib/llm_api/hazo_llm_document_text.js.map +1 -1
  62. package/dist/lib/llm_api/hazo_llm_dynamic_data_extract.d.ts.map +1 -1
  63. package/dist/lib/llm_api/hazo_llm_dynamic_data_extract.js +19 -1
  64. package/dist/lib/llm_api/hazo_llm_dynamic_data_extract.js.map +1 -1
  65. package/dist/lib/llm_api/hazo_llm_embed.d.ts +10 -0
  66. package/dist/lib/llm_api/hazo_llm_embed.d.ts.map +1 -0
  67. package/dist/lib/llm_api/hazo_llm_embed.js +80 -0
  68. package/dist/lib/llm_api/hazo_llm_embed.js.map +1 -0
  69. package/dist/lib/llm_api/hazo_llm_image_image.d.ts.map +1 -1
  70. package/dist/lib/llm_api/hazo_llm_image_image.js +56 -14
  71. package/dist/lib/llm_api/hazo_llm_image_image.js.map +1 -1
  72. package/dist/lib/llm_api/hazo_llm_image_text.d.ts.map +1 -1
  73. package/dist/lib/llm_api/hazo_llm_image_text.js +56 -14
  74. package/dist/lib/llm_api/hazo_llm_image_text.js.map +1 -1
  75. package/dist/lib/llm_api/hazo_llm_prompt_chain.d.ts.map +1 -1
  76. package/dist/lib/llm_api/hazo_llm_prompt_chain.js +17 -1
  77. package/dist/lib/llm_api/hazo_llm_prompt_chain.js.map +1 -1
  78. package/dist/lib/llm_api/hazo_llm_text_image.d.ts.map +1 -1
  79. package/dist/lib/llm_api/hazo_llm_text_image.js +56 -14
  80. package/dist/lib/llm_api/hazo_llm_text_image.js.map +1 -1
  81. package/dist/lib/llm_api/hazo_llm_text_text.d.ts.map +1 -1
  82. package/dist/lib/llm_api/hazo_llm_text_text.js +90 -15
  83. package/dist/lib/llm_api/hazo_llm_text_text.js.map +1 -1
  84. package/dist/lib/llm_api/index.d.ts +29 -1
  85. package/dist/lib/llm_api/index.d.ts.map +1 -1
  86. package/dist/lib/llm_api/index.js +433 -6
  87. package/dist/lib/llm_api/index.js.map +1 -1
  88. package/dist/lib/llm_api/prompt_parts_helper.d.ts +15 -0
  89. package/dist/lib/llm_api/prompt_parts_helper.d.ts.map +1 -0
  90. package/dist/lib/llm_api/prompt_parts_helper.js +9 -0
  91. package/dist/lib/llm_api/prompt_parts_helper.js.map +1 -0
  92. package/dist/lib/llm_api/types.d.ts +187 -2
  93. package/dist/lib/llm_api/types.d.ts.map +1 -1
  94. package/dist/lib/llm_api/types.js +4 -0
  95. package/dist/lib/llm_api/types.js.map +1 -1
  96. package/dist/lib/maintenance/purge_log_job.d.ts +23 -0
  97. package/dist/lib/maintenance/purge_log_job.d.ts.map +1 -0
  98. package/dist/lib/maintenance/purge_log_job.js +42 -0
  99. package/dist/lib/maintenance/purge_log_job.js.map +1 -0
  100. package/dist/lib/observability/log_context.d.ts +15 -0
  101. package/dist/lib/observability/log_context.d.ts.map +1 -0
  102. package/dist/lib/observability/log_context.js +32 -0
  103. package/dist/lib/observability/log_context.js.map +1 -0
  104. package/dist/lib/observability/log_writer.d.ts +35 -0
  105. package/dist/lib/observability/log_writer.d.ts.map +1 -0
  106. package/dist/lib/observability/log_writer.js +106 -0
  107. package/dist/lib/observability/log_writer.js.map +1 -0
  108. package/dist/lib/observability/queries.d.ts +15 -0
  109. package/dist/lib/observability/queries.d.ts.map +1 -0
  110. package/dist/lib/observability/queries.js +78 -0
  111. package/dist/lib/observability/queries.js.map +1 -0
  112. package/dist/lib/observability/types.d.ts +77 -0
  113. package/dist/lib/observability/types.d.ts.map +1 -0
  114. package/dist/lib/observability/types.js +8 -0
  115. package/dist/lib/observability/types.js.map +1 -0
  116. package/dist/lib/pricing/pricing.d.ts +49 -0
  117. package/dist/lib/pricing/pricing.d.ts.map +1 -0
  118. package/dist/lib/pricing/pricing.js +153 -0
  119. package/dist/lib/pricing/pricing.js.map +1 -0
  120. package/dist/lib/pricing/pricing.json +75 -0
  121. package/dist/lib/pricing/types.d.ts +58 -0
  122. package/dist/lib/pricing/types.d.ts.map +1 -0
  123. package/dist/lib/pricing/types.js +8 -0
  124. package/dist/lib/pricing/types.js.map +1 -0
  125. package/dist/lib/providers/anthropic/anthropic_client.d.ts +71 -0
  126. package/dist/lib/providers/anthropic/anthropic_client.d.ts.map +1 -0
  127. package/dist/lib/providers/anthropic/anthropic_client.js +134 -0
  128. package/dist/lib/providers/anthropic/anthropic_client.js.map +1 -0
  129. package/dist/lib/providers/anthropic/anthropic_provider.d.ts +60 -0
  130. package/dist/lib/providers/anthropic/anthropic_provider.d.ts.map +1 -0
  131. package/dist/lib/providers/anthropic/anthropic_provider.js +273 -0
  132. package/dist/lib/providers/anthropic/anthropic_provider.js.map +1 -0
  133. package/dist/lib/providers/anthropic/anthropic_response_to_usage.d.ts +21 -0
  134. package/dist/lib/providers/anthropic/anthropic_response_to_usage.d.ts.map +1 -0
  135. package/dist/lib/providers/anthropic/anthropic_response_to_usage.js +46 -0
  136. package/dist/lib/providers/anthropic/anthropic_response_to_usage.js.map +1 -0
  137. package/dist/lib/providers/anthropic/index.d.ts +3 -0
  138. package/dist/lib/providers/anthropic/index.d.ts.map +1 -0
  139. package/dist/lib/providers/anthropic/index.js +2 -0
  140. package/dist/lib/providers/anthropic/index.js.map +1 -0
  141. package/dist/lib/providers/deepseek/deepseek_client.d.ts +55 -0
  142. package/dist/lib/providers/deepseek/deepseek_client.d.ts.map +1 -0
  143. package/dist/lib/providers/deepseek/deepseek_client.js +129 -0
  144. package/dist/lib/providers/deepseek/deepseek_client.js.map +1 -0
  145. package/dist/lib/providers/deepseek/deepseek_provider.d.ts +50 -0
  146. package/dist/lib/providers/deepseek/deepseek_provider.d.ts.map +1 -0
  147. package/dist/lib/providers/deepseek/deepseek_provider.js +147 -0
  148. package/dist/lib/providers/deepseek/deepseek_provider.js.map +1 -0
  149. package/dist/lib/providers/deepseek/deepseek_response_to_usage.d.ts +21 -0
  150. package/dist/lib/providers/deepseek/deepseek_response_to_usage.d.ts.map +1 -0
  151. package/dist/lib/providers/deepseek/deepseek_response_to_usage.js +40 -0
  152. package/dist/lib/providers/deepseek/deepseek_response_to_usage.js.map +1 -0
  153. package/dist/lib/providers/deepseek/index.d.ts +3 -0
  154. package/dist/lib/providers/deepseek/index.d.ts.map +1 -0
  155. package/dist/lib/providers/deepseek/index.js +2 -0
  156. package/dist/lib/providers/deepseek/index.js.map +1 -0
  157. package/dist/lib/providers/gemini/gemini_provider.d.ts.map +1 -1
  158. package/dist/lib/providers/gemini/gemini_provider.js +40 -4
  159. package/dist/lib/providers/gemini/gemini_provider.js.map +1 -1
  160. package/dist/lib/providers/gemini/gemini_response_to_usage.d.ts +37 -0
  161. package/dist/lib/providers/gemini/gemini_response_to_usage.d.ts.map +1 -0
  162. package/dist/lib/providers/gemini/gemini_response_to_usage.js +49 -0
  163. package/dist/lib/providers/gemini/gemini_response_to_usage.js.map +1 -0
  164. package/dist/lib/providers/index.d.ts +3 -0
  165. package/dist/lib/providers/index.d.ts.map +1 -1
  166. package/dist/lib/providers/index.js +3 -0
  167. package/dist/lib/providers/index.js.map +1 -1
  168. package/dist/lib/providers/openai/index.d.ts +3 -0
  169. package/dist/lib/providers/openai/index.d.ts.map +1 -0
  170. package/dist/lib/providers/openai/index.js +2 -0
  171. package/dist/lib/providers/openai/index.js.map +1 -0
  172. package/dist/lib/providers/openai/openai_client.d.ts +99 -0
  173. package/dist/lib/providers/openai/openai_client.d.ts.map +1 -0
  174. package/dist/lib/providers/openai/openai_client.js +187 -0
  175. package/dist/lib/providers/openai/openai_client.js.map +1 -0
  176. package/dist/lib/providers/openai/openai_provider.d.ts +66 -0
  177. package/dist/lib/providers/openai/openai_provider.d.ts.map +1 -0
  178. package/dist/lib/providers/openai/openai_provider.js +297 -0
  179. package/dist/lib/providers/openai/openai_provider.js.map +1 -0
  180. package/dist/lib/providers/openai/openai_response_to_usage.d.ts +21 -0
  181. package/dist/lib/providers/openai/openai_response_to_usage.d.ts.map +1 -0
  182. package/dist/lib/providers/openai/openai_response_to_usage.js +50 -0
  183. package/dist/lib/providers/openai/openai_response_to_usage.js.map +1 -0
  184. package/dist/lib/providers/qwen/qwen_provider.d.ts.map +1 -1
  185. package/dist/lib/providers/qwen/qwen_provider.js +52 -5
  186. package/dist/lib/providers/qwen/qwen_provider.js.map +1 -1
  187. package/dist/lib/providers/qwen/qwen_response_to_usage.d.ts +36 -0
  188. package/dist/lib/providers/qwen/qwen_response_to_usage.d.ts.map +1 -0
  189. package/dist/lib/providers/qwen/qwen_response_to_usage.js +50 -0
  190. package/dist/lib/providers/qwen/qwen_response_to_usage.js.map +1 -0
  191. package/dist/lib/providers/types.d.ts +16 -6
  192. package/dist/lib/providers/types.d.ts.map +1 -1
  193. package/dist/lib/providers/types.js +1 -0
  194. package/dist/lib/providers/types.js.map +1 -1
  195. package/dist/lib/utils.d.ts +13 -0
  196. package/dist/lib/utils.d.ts.map +1 -0
  197. package/dist/lib/utils.js +16 -0
  198. package/dist/lib/utils.js.map +1 -0
  199. package/dist/server.d.ts +16 -2
  200. package/dist/server.d.ts.map +1 -1
  201. package/dist/server.js +29 -2
  202. package/dist/server.js.map +1 -1
  203. package/migrations/hazo_llm_api_log.sql +69 -0
  204. package/package.json +19 -5
package/README.md CHANGED
@@ -19,13 +19,18 @@ A wrapper package for calling different LLMs with built-in prompt management and
19
19
  | `hazo_llm_dynamic_data_extract` | Initial Prompt | Merged Results | Dynamic chain where next prompt is determined by JSON output |
20
20
 
21
21
  **Features:**
22
- - **Multi-Provider Support**: Use Gemini, Qwen, or add your own LLM providers
22
+ - **Multi-Provider Support**: Use Gemini, Qwen, Anthropic, OpenAI, DeepSeek, or add your own LLM providers
23
+ - **Cascade Fallback**: Automatically retry across providers on failure with configurable cascade
24
+ - **Observability**: Per-call usage metrics (tokens, cost, latency) with automatic log table and query API
25
+ - **Cost Cap**: Opt-in per-session spend limit with block or warn modes
26
+ - **Embeddings**: Single and batch text embeddings via OpenAI with LRU/persistent cache
27
+ - **Admin UI**: `LLMCostDashboard` and `LLMCallInspector` React components for spend monitoring
23
28
  - **Prompt Management**: Store and retrieve prompts from a SQLite database with LRU caching
24
29
  - **Variable Substitution**: Replace `$variables` in prompts with dynamic values
25
- - **Multi-modal Support**: Handle text and images seamlessly
30
+ - **Multi-modal Support**: Handle text, images, and documents (PDFs) seamlessly
26
31
  - **Extensible Architecture**: Provider-based design with simple registration system
27
32
  - **Type-Safe**: Full TypeScript support with comprehensive type definitions
28
- - **Auto-Initialization**: Database initializes automatically on import
33
+ - **Auto-Initialization**: Database and log table initialize automatically on first call
29
34
  - **Flexible Logging**: Pass any compatible logger (e.g., `hazo_logs`, Winston, or custom)
30
35
  - **Lifecycle Hooks**: Monitor, log, and analyze LLM calls with beforeRequest, afterResponse, and onError hooks
31
36
  - **Streaming Support**: Real-time streaming responses for text generation
@@ -643,7 +648,24 @@ interface LLMResponse {
643
648
  image_b64?: string; // Generated image (base64)
644
649
  image_mime_type?: string; // MIME type of generated image
645
650
  error?: string; // Error message if failed
651
+ error_info?: LLMError; // Structured error (code, message, retryable)
652
+ error_code?: LLMErrorCode; // Shorthand alias for error_info.code
646
653
  raw_response?: unknown; // Raw API response
654
+ usage?: UsageInfo; // Tokens, cost, latency, model, finish_reason, attempts
655
+ }
656
+ ```
657
+
658
+ `UsageInfo` includes:
659
+ ```typescript
660
+ interface UsageInfo {
661
+ input_tokens?: number;
662
+ output_tokens?: number;
663
+ cost_usd?: number;
664
+ latency_ms?: number;
665
+ finish_reason?: FinishReason;
666
+ model?: string;
667
+ provider?: string;
668
+ attempts?: UsageAttempt[]; // Failed cascade attempts before success
647
669
  }
648
670
  ```
649
671
 
@@ -692,11 +714,43 @@ text_temperature=0.8
692
714
 
693
715
  ### Supported Providers
694
716
 
695
- | Provider | Capabilities | Configuration Required |
696
- |----------|-------------|------------------------|
697
- | Gemini | text_text, image_text, text_image, image_image | GEMINI_API_KEY in .env.local |
698
- | Qwen | text_text, image_text, text_image, image_image | QWEN_API_KEY in .env.local |
699
- | Custom | Define your own | Implement LLMProvider interface |
717
+ | Provider | Capabilities | Config Section | Env Var |
718
+ |----------|-------------|----------------|---------|
719
+ | Gemini | text_text, image_text, text_image, image_image, document_text | `[llm_gemini]` | `GEMINI_API_KEY` |
720
+ | Qwen | text_text, image_text, text_image, image_image | `[llm_qwen]` | `QWEN_API_KEY` |
721
+ | Anthropic | text_text, image_text, document_text, text_text_stream | `[llm_anthropic]` | `ANTHROPIC_API_KEY` |
722
+ | OpenAI | text_text, image_text, text_image, text_text_stream, embed | `[llm_openai]` | `OPENAI_API_KEY` |
723
+ | DeepSeek | text_text, text_text_stream | `[llm_deepseek]` | `DEEPSEEK_API_KEY` |
724
+ | Custom | Define your own | — | Implement LLMProvider interface |
725
+
726
+ **Provider config example** — add each enabled provider to your `config/hazo_llm_api_config.ini`:
727
+
728
+ ```ini
729
+ [llm]
730
+ enabled_llms=["gemini", "anthropic", "openai", "deepseek"]
731
+ primary_llm=anthropic
732
+
733
+ [llm_anthropic]
734
+ api_url=https://api.anthropic.com/v1/messages
735
+ api_version=2023-06-01
736
+ model_text_text=claude-sonnet-4-6
737
+ model_image_text=claude-sonnet-4-6
738
+ model_document_text=claude-sonnet-4-6
739
+ text_max_tokens=8192
740
+
741
+ [llm_openai]
742
+ api_url=https://api.openai.com/v1/chat/completions
743
+ api_url_image=https://api.openai.com/v1/images/generations
744
+ api_url_embed=https://api.openai.com/v1/embeddings
745
+ model_text_text=gpt-4o
746
+ model_image_text=gpt-4o
747
+ model_text_image=gpt-image-1
748
+ model_embed=text-embedding-3-small
749
+
750
+ [llm_deepseek]
751
+ api_url=https://api.deepseek.com/v1/chat/completions
752
+ model_text_text=deepseek-chat
753
+ ```
700
754
 
701
755
  See `TECHDOC.md` for instructions on adding custom providers.
702
756
 
@@ -1438,6 +1492,265 @@ The PromptEditor component now displays:
1438
1492
 
1439
1493
  No code changes are required if you're using the `PromptEditor` component - it automatically supports the new structure.
1440
1494
 
1495
+ ## Observability
1496
+
1497
+ Every LLM call automatically writes one row to `hazo_llm_api_log` (SQLite or PostgreSQL — created on first call, no manual migration needed). The row contains provider, model, service_type, token counts, `cost_usd`, `latency_ms`, `session_id`, `reference`, `error_code`, and a `context_json` blob.
1498
+
1499
+ ### Response Usage
1500
+
1501
+ ```typescript
1502
+ const response = await hazo_llm_text_text({ prompt: 'Hello' });
1503
+
1504
+ if (response.usage) {
1505
+ console.log(`Cost: $${response.usage.cost_usd?.toFixed(6)}`);
1506
+ console.log(`Tokens: ${response.usage.input_tokens}in / ${response.usage.output_tokens}out`);
1507
+ console.log(`Latency: ${response.usage.latency_ms}ms`);
1508
+ }
1509
+ ```
1510
+
1511
+ ### Querying the Log
1512
+
1513
+ ```typescript
1514
+ import { get_llm_usage_summary, get_llm_call_detail } from 'hazo_llm_api/server';
1515
+
1516
+ // Aggregate stats — group by provider over the last 7 days
1517
+ const summary = await get_llm_usage_summary({
1518
+ from: '2026-05-18',
1519
+ to: '2026-05-25',
1520
+ group_by: 'provider',
1521
+ limit: 100,
1522
+ });
1523
+
1524
+ // Single call drill-down
1525
+ const detail = await get_llm_call_detail('some-uuid-here');
1526
+ ```
1527
+
1528
+ ### Pricing Overrides and Retroactive Recalculation
1529
+
1530
+ ```typescript
1531
+ import { update_pricing, recompute_costs } from 'hazo_llm_api/server';
1532
+
1533
+ // Override pricing at runtime
1534
+ update_pricing('anthropic/claude-sonnet-4-6', {
1535
+ kind: 'text',
1536
+ input_per_1m: 3.0,
1537
+ output_per_1m: 15.0,
1538
+ });
1539
+
1540
+ // Retroactively recalculate costs after a pricing correction
1541
+ const result = await recompute_costs({
1542
+ from: '2026-05-01',
1543
+ to: '2026-05-25',
1544
+ models: ['claude-sonnet-4-6'],
1545
+ dry_run: false, // Set true to preview without writing
1546
+ });
1547
+ console.log(`Updated ${result.updated_count} rows`);
1548
+ ```
1549
+
1550
+ ### Disabling the Log Writer
1551
+
1552
+ ```typescript
1553
+ await initialize_llm_api({
1554
+ logger,
1555
+ api_log: { enabled: false }, // No rows written to hazo_llm_api_log
1556
+ });
1557
+ ```
1558
+
1559
+ ### hazo_logs Integration
1560
+
1561
+ If `hazo_logs ^1.1.0` is installed, `session_id` and `reference` are read automatically from the async local storage context — no extra configuration required.
1562
+
1563
+ ---
1564
+
1565
+ ## Cascade Fallback
1566
+
1567
+ Pass `providers` on any call to define an ordered fallback list. The call tries each provider in sequence, stopping on the first success. Failed attempts are recorded in `response.usage.attempts`.
1568
+
1569
+ ```typescript
1570
+ // Try Anthropic first, fall back to OpenAI, then Gemini
1571
+ const response = await hazo_llm_text_text(
1572
+ { prompt: 'Hello', providers: ['anthropic', 'openai', 'gemini'] },
1573
+ );
1574
+ ```
1575
+
1576
+ ### Default Cascade Config
1577
+
1578
+ Set a default cascade for all calls at init time:
1579
+
1580
+ ```typescript
1581
+ import { DEFAULT_CASCADE_ON_CODES } from 'hazo_llm_api/server';
1582
+
1583
+ await initialize_llm_api({
1584
+ logger,
1585
+ cascade: {
1586
+ providers: ['anthropic', 'openai'],
1587
+ on_codes: DEFAULT_CASCADE_ON_CODES, // RATE_LIMITED, NETWORK_ERROR, TIMEOUT
1588
+ timeout_ms_per_attempt: 30000,
1589
+ },
1590
+ });
1591
+ ```
1592
+
1593
+ Per-call `providers` always overrides the init-time cascade config.
1594
+
1595
+ ---
1596
+
1597
+ ## Embeddings
1598
+
1599
+ Generate single or batch text embeddings (requires OpenAI provider):
1600
+
1601
+ ```typescript
1602
+ import { hazo_llm_embed } from 'hazo_llm_api/server';
1603
+
1604
+ // Single text
1605
+ const result = await hazo_llm_embed({ text: 'Hello world' });
1606
+ if (result.success) {
1607
+ console.log(result.vectors![0]); // number[]
1608
+ console.log(`Dimensions: ${result.dimensions}`);
1609
+ }
1610
+
1611
+ // Batch
1612
+ const batch = await hazo_llm_embed({
1613
+ text: ['First sentence', 'Second sentence', 'Third sentence'],
1614
+ model: 'text-embedding-3-large', // optional override
1615
+ });
1616
+ ```
1617
+
1618
+ Repeated calls with the same text are served from an in-memory LRU cache. For persistent caching, provide a `Keyv` instance at init time:
1619
+
1620
+ ```typescript
1621
+ import Keyv from 'keyv';
1622
+ import KeyvSqlite from '@keyv/sqlite';
1623
+
1624
+ await initialize_llm_api({
1625
+ logger,
1626
+ embed_cache: {
1627
+ max_size: 5000,
1628
+ keyv: new Keyv({ store: new KeyvSqlite('sqlite://embed_cache.sqlite') }),
1629
+ },
1630
+ });
1631
+ ```
1632
+
1633
+ ---
1634
+
1635
+ ## Prompt Caching (Anthropic)
1636
+
1637
+ Pass `prompt_parts` instead of `prompt` to hint which parts should be cached at the provider level. Anthropic supports prompt caching via `cache_control`. Other providers concatenate parts as plain text.
1638
+
1639
+ ```typescript
1640
+ const response = await hazo_llm_text_text({
1641
+ prompt_parts: [
1642
+ { text: long_system_context, cache: true }, // Cached by Anthropic
1643
+ { text: user_message }, // Not cached
1644
+ ],
1645
+ });
1646
+ ```
1647
+
1648
+ ---
1649
+
1650
+ ## Cost Cap
1651
+
1652
+ Prevent runaway spend by configuring a per-session cost cap. The gate reads `SUM(cost_usd)` from `hazo_llm_api_log` before each call.
1653
+
1654
+ ```typescript
1655
+ await initialize_llm_api({
1656
+ logger,
1657
+ cost_cap: {
1658
+ enabled: true,
1659
+ window: 'session', // 'session' | 'day' | 'hour'
1660
+ on_exceeded: 'block', // 'block' | 'warn'
1661
+ get_user_cap: async ({ session_id }) => {
1662
+ // Return the USD cap for this session
1663
+ return await db.getUserSpendLimit(session_id);
1664
+ },
1665
+ cost_cap_exceeded: async ({ session_id, current_usd, cap_usd }) => {
1666
+ // Called only in 'warn' mode
1667
+ console.warn(`Session ${session_id} exceeded cap: $${current_usd} of $${cap_usd}`);
1668
+ },
1669
+ },
1670
+ });
1671
+ ```
1672
+
1673
+ In `block` mode, calls that would exceed the cap return immediately with `error_code: 'COST_LIMIT_EXCEEDED'`. In `warn` mode, the `cost_cap_exceeded` hook fires and the call proceeds.
1674
+
1675
+ Requires `hazo_logs ^1.1.0` for `session_id` ALS propagation.
1676
+
1677
+ ---
1678
+
1679
+ ## Admin UI Components
1680
+
1681
+ Import dashboard components for monitoring spend and inspecting individual calls. Requires `hazo_ui ^2.17.0`.
1682
+
1683
+ ### LLMCostDashboard
1684
+
1685
+ ```typescript
1686
+ import { LLMCostDashboard } from 'hazo_llm_api';
1687
+ import type { FetchSummaryFn } from 'hazo_llm_api';
1688
+
1689
+ // Build the fetch function using your framework's API routes
1690
+ const fetch_summary: FetchSummaryFn = async (opts) => {
1691
+ const params = new URLSearchParams({ ...opts });
1692
+ const res = await fetch(`/api/llm-summary?${params}`);
1693
+ return res.json();
1694
+ };
1695
+
1696
+ function MonitoringPage() {
1697
+ return <LLMCostDashboard fetch_summary={fetch_summary} />;
1698
+ }
1699
+ ```
1700
+
1701
+ The component provides date-range filters, group-by selector (`date` / `provider` / `model` / `service_type`), an SVG time-series chart (when `group_by=date`), and a summary table with totals.
1702
+
1703
+ ### LLMCallInspector
1704
+
1705
+ ```typescript
1706
+ import { LLMCallInspector } from 'hazo_llm_api';
1707
+ import type { FetchDetailFn } from 'hazo_llm_api';
1708
+
1709
+ const fetch_detail: FetchDetailFn = async (id) => {
1710
+ const res = await fetch(`/api/llm-call-detail?id=${id}`);
1711
+ return res.json();
1712
+ };
1713
+
1714
+ function CallDetailPage({ call_id }: { call_id: string }) {
1715
+ return <LLMCallInspector callId={call_id} fetch_detail={fetch_detail} />;
1716
+ }
1717
+ ```
1718
+
1719
+ ---
1720
+
1721
+ ## Maintenance
1722
+
1723
+ Register the log purge job with `hazo_jobs` to automatically delete old log rows:
1724
+
1725
+ ```typescript
1726
+ import { llm_api_purge_log_job } from 'hazo_llm_api/server';
1727
+ import { register_job } from 'hazo_jobs/server';
1728
+
1729
+ // Register the job — hazo_jobs scheduler calls it on your configured schedule
1730
+ register_job(llm_api_purge_log_job);
1731
+
1732
+ // Payload when triggering the job
1733
+ // { retention_days: 90 } — rows older than 90 days are deleted
1734
+ ```
1735
+
1736
+ The job requires `hazo_jobs ^0.10.0` as a peer dependency.
1737
+
1738
+ ---
1739
+
1740
+ ## Environment Variables
1741
+
1742
+ | Variable | Provider | Required |
1743
+ |----------|----------|----------|
1744
+ | `GEMINI_API_KEY` | Gemini | When `gemini` is in `enabled_llms` |
1745
+ | `QWEN_API_KEY` | Qwen | When `qwen` is in `enabled_llms` |
1746
+ | `ANTHROPIC_API_KEY` | Anthropic | When `anthropic` is in `enabled_llms` |
1747
+ | `OPENAI_API_KEY` | OpenAI | When `openai` is in `enabled_llms` |
1748
+ | `DEEPSEEK_API_KEY` | DeepSeek | When `deepseek` is in `enabled_llms` |
1749
+
1750
+ All keys go in `.env.local` (never committed to git).
1751
+
1752
+ ---
1753
+
1441
1754
  ## Support
1442
1755
 
1443
1756
  For issues and questions, please visit the [GitHub Issues](https://github.com/pub12/hazo_llm_api/issues) page.
@@ -215,6 +215,39 @@ capabilities=["text_text", "image_text", "text_image", "image_image"]
215
215
  # Generation stops when any of these sequences is encountered
216
216
  ; image_stop=["###END"]
217
217
 
218
+ [llm_anthropic]
219
+ ; Anthropic API configuration
220
+ ; Set ANTHROPIC_API_KEY in .env.local
221
+ ; api_key_env = ANTHROPIC_API_KEY
222
+ api_url = https://api.anthropic.com/v1/messages
223
+ api_version = 2023-06-01
224
+ model_text_text = claude-sonnet-4-6
225
+ model_image_text = claude-sonnet-4-6
226
+ model_document_text = claude-sonnet-4-6
227
+ text_max_tokens = 8192
228
+ ; capabilities = text_text, image_text, document_text, text_text_stream
229
+
230
+ [llm_openai]
231
+ ; OpenAI API configuration
232
+ ; Set OPENAI_API_KEY in .env.local
233
+ ; api_key_env = OPENAI_API_KEY
234
+ api_url = https://api.openai.com/v1/chat/completions
235
+ api_url_image = https://api.openai.com/v1/images/generations
236
+ api_url_embed = https://api.openai.com/v1/embeddings
237
+ model_text_text = gpt-4o
238
+ model_image_text = gpt-4o
239
+ model_text_image = gpt-image-1
240
+ model_embed = text-embedding-3-small
241
+ ; capabilities = text_text, image_text, text_image, text_text_stream, embed
242
+
243
+ [llm_deepseek]
244
+ ; DeepSeek API configuration
245
+ ; Set DEEPSEEK_API_KEY in .env.local
246
+ ; api_key_env = DEEPSEEK_API_KEY
247
+ api_url = https://api.deepseek.com/v1/chat/completions
248
+ model_text_text = deepseek-chat
249
+ ; capabilities = text_text, text_text_stream
250
+
218
251
  [database]
219
252
  # Database configuration
220
253
  # Enable WAL mode for better SQLite performance
@@ -5,4 +5,6 @@
5
5
  */
6
6
  export * from './layout/index.js';
7
7
  export * from './prompt_editor/index.js';
8
+ export * from './llm_cost_dashboard/index.js';
9
+ export * from './llm_call_inspector/index.js';
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC"}
@@ -5,4 +5,6 @@
5
5
  */
6
6
  export * from './layout/index.js';
7
7
  export * from './prompt_editor/index.js';
8
+ export * from './llm_cost_dashboard/index.js';
9
+ export * from './llm_call_inspector/index.js';
8
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * LLMCallInspector — re-export
3
+ */
4
+ export { LLMCallInspector } from './llm_call_inspector.js';
5
+ export type { LLMCallInspectorProps } from './llm_call_inspector.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/llm_call_inspector/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * LLMCallInspector — re-export
3
+ */
4
+ export { LLMCallInspector } from './llm_call_inspector.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/llm_call_inspector/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * LLMCallInspector Component
3
+ *
4
+ * Displays detailed information about a single LLM API call log row.
5
+ * Framework-agnostic: accepts a fetch_detail prop instead of accessing
6
+ * the database directly.
7
+ */
8
+ import type { FetchDetailFn } from '../../lib/observability/types.js';
9
+ export interface LLMCallInspectorProps {
10
+ /** ID of the call to inspect. null = show placeholder. */
11
+ call_id: string | null;
12
+ /** Async function to fetch call detail */
13
+ fetch_detail: FetchDetailFn;
14
+ /** Optional CSS class */
15
+ className?: string;
16
+ }
17
+ export declare function LLMCallInspector({ call_id, fetch_detail, className }: LLMCallInspectorProps): import("react/jsx-runtime").JSX.Element;
18
+ //# sourceMappingURL=llm_call_inspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm_call_inspector.d.ts","sourceRoot":"","sources":["../../../src/components/llm_call_inspector/llm_call_inspector.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,kCAAkC,CAAC;AAMjF,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0CAA0C;IAC1C,YAAY,EAAE,aAAa,CAAC;IAC5B,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4CD,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,qBAAqB,2CAsN3F"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * LLMCallInspector Component
3
+ *
4
+ * Displays detailed information about a single LLM API call log row.
5
+ * Framework-agnostic: accepts a fetch_detail prop instead of accessing
6
+ * the database directly.
7
+ */
8
+ 'use client';
9
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
10
+ import { useState, useEffect } from 'react';
11
+ import { cn } from '../../lib/utils.js';
12
+ // ---------------------------------------------------------------------------
13
+ // Helpers
14
+ // ---------------------------------------------------------------------------
15
+ function fmt_cost(value) {
16
+ if (value == null)
17
+ return '$0.0000';
18
+ return `$${Number(value).toFixed(4)}`;
19
+ }
20
+ function fmt_id_short(id) {
21
+ if (id.length <= 15)
22
+ return id;
23
+ return `${id.slice(0, 12)}...`;
24
+ }
25
+ function is_success(row) {
26
+ return row.success === 1 || row.success === true;
27
+ }
28
+ function DlRow({ label, value }) {
29
+ return (_jsxs(_Fragment, { children: [_jsx("dt", { className: "cls_dt text-xs font-medium uppercase tracking-wide text-gray-500", children: label }), _jsx("dd", { className: "cls_dd text-sm text-gray-900", children: value })] }));
30
+ }
31
+ // ---------------------------------------------------------------------------
32
+ // Component
33
+ // ---------------------------------------------------------------------------
34
+ export function LLMCallInspector({ call_id, fetch_detail, className }) {
35
+ const [loading, set_loading] = useState(false);
36
+ const [error, set_error] = useState(null);
37
+ const [row, set_row] = useState(null);
38
+ useEffect(() => {
39
+ if (!call_id) {
40
+ set_row(null);
41
+ set_error(null);
42
+ return;
43
+ }
44
+ let cancelled = false;
45
+ async function load() {
46
+ set_loading(true);
47
+ set_error(null);
48
+ try {
49
+ const result = await fetch_detail(call_id);
50
+ if (!cancelled) {
51
+ set_row(result);
52
+ }
53
+ }
54
+ catch (err) {
55
+ if (!cancelled) {
56
+ set_error(err instanceof Error ? err.message : 'Failed to load call details');
57
+ }
58
+ }
59
+ finally {
60
+ if (!cancelled) {
61
+ set_loading(false);
62
+ }
63
+ }
64
+ }
65
+ load();
66
+ return () => {
67
+ cancelled = true;
68
+ };
69
+ // fetch_detail intentionally omitted — callers should pass a stable reference (useCallback)
70
+ }, [call_id]);
71
+ // ---------------------------------------------------------------------------
72
+ // Placeholder
73
+ // ---------------------------------------------------------------------------
74
+ if (!call_id) {
75
+ return (_jsx("div", { className: cn('cls_llm_call_inspector flex items-center justify-center rounded-lg border border-dashed border-gray-300 bg-gray-50 px-8 py-16', className), children: _jsx("p", { className: "text-sm text-gray-400", children: "Select a call to inspect" }) }));
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Loading state
79
+ // ---------------------------------------------------------------------------
80
+ if (loading) {
81
+ return (_jsx("div", { className: cn('cls_llm_call_inspector flex items-center justify-center rounded-lg border border-gray-200 bg-white px-8 py-16', className), children: _jsx("p", { className: "text-sm text-gray-500", children: "Loading call details..." }) }));
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Error state
85
+ // ---------------------------------------------------------------------------
86
+ if (error) {
87
+ return (_jsxs("div", { className: cn('cls_llm_call_inspector rounded-lg border border-red-200 bg-red-50 px-4 py-6', className), children: [_jsx("p", { className: "text-sm text-red-700", children: "Failed to load call details" }), _jsx("p", { className: "mt-1 text-xs text-red-500", children: error })] }));
88
+ }
89
+ // ---------------------------------------------------------------------------
90
+ // Not found state (fetch returned null)
91
+ // ---------------------------------------------------------------------------
92
+ if (!row) {
93
+ return (_jsx("div", { className: cn('cls_llm_call_inspector flex items-center justify-center rounded-lg border border-gray-200 bg-white px-8 py-16', className), children: _jsx("p", { className: "text-sm text-gray-400", children: "Call not found" }) }));
94
+ }
95
+ const success = is_success(row);
96
+ // ---------------------------------------------------------------------------
97
+ // Full detail view
98
+ // ---------------------------------------------------------------------------
99
+ return (_jsxs("div", { className: cn('cls_llm_call_inspector space-y-4 rounded-lg border border-gray-200 bg-white p-5', className), children: [_jsxs("div", { className: "cls_inspector_header flex items-center gap-3", children: [_jsx("code", { className: "cls_call_id rounded bg-gray-100 px-2 py-1 font-mono text-sm text-gray-800", children: fmt_id_short(row.id) }), _jsx("span", { className: cn('cls_status_badge inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium', success
100
+ ? 'bg-green-100 text-green-800'
101
+ : 'bg-red-100 text-red-800'), children: success ? 'Success' : 'Failed' }), row.created_at && (_jsx("span", { className: "ml-auto text-xs text-gray-400", children: row.created_at }))] }), _jsx("div", { className: "cls_metrics_grid", children: _jsxs("dl", { className: "grid grid-cols-2 gap-x-4 gap-y-2", children: [_jsx(DlRow, { label: "Provider", value: row.provider || '—' }), _jsx(DlRow, { label: "Model", value: row.model || '—' }), _jsx(DlRow, { label: "Service Type", value: row.service_type || '—' }), _jsx(DlRow, { label: "Cost", value: fmt_cost(row.cost_usd) }), _jsx(DlRow, { label: "Latency", value: row.latency_ms != null ? `${row.latency_ms} ms` : '—' })] }) }), _jsxs("div", { className: "cls_tokens_section", children: [_jsx("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-gray-500", children: "Tokens" }), _jsxs("dl", { className: "grid grid-cols-2 gap-x-4 gap-y-1.5 sm:grid-cols-4", children: [row.tokens_input != null && (_jsx(DlRow, { label: "Input", value: row.tokens_input.toLocaleString() })), row.tokens_output != null && (_jsx(DlRow, { label: "Output", value: row.tokens_output.toLocaleString() })), row.tokens_cached_input != null && (_jsx(DlRow, { label: "Cached Input", value: row.tokens_cached_input.toLocaleString() })), row.tokens_total != null && (_jsx(DlRow, { label: "Total", value: row.tokens_total.toLocaleString() }))] })] }), row.error_code && (_jsxs("div", { className: "cls_error_section rounded-md border border-red-200 bg-red-50 px-4 py-3", children: [_jsx("p", { className: "text-sm font-medium text-red-800", children: row.error_code }), row.error_message && (_jsx("p", { className: "mt-0.5 text-sm text-red-700", children: row.error_message }))] })), (row.session_id || row.reference || row.prompt_area || row.prompt_key) && (_jsxs("div", { className: "cls_context_section", children: [_jsx("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-gray-500", children: "Context" }), _jsxs("dl", { className: "grid grid-cols-2 gap-x-4 gap-y-1.5", children: [row.session_id && _jsx(DlRow, { label: "Session ID", value: row.session_id }), row.reference && _jsx(DlRow, { label: "Reference", value: row.reference }), row.prompt_area && _jsx(DlRow, { label: "Prompt Area", value: row.prompt_area }), row.prompt_key && _jsx(DlRow, { label: "Prompt Key", value: row.prompt_key })] })] })), _jsxs("p", { className: "cls_attempts text-xs text-gray-500", children: [row.attempts_count ?? 0, " attempt", (row.attempts_count ?? 0) === 1 ? '' : 's'] }), _jsxs("details", { className: "cls_raw_json_details rounded border border-gray-200", children: [_jsx("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-gray-600 hover:bg-gray-50", children: "Raw JSON" }), _jsx("pre", { className: "cls_raw_json_pre overflow-x-auto px-3 py-2 text-xs text-gray-700", children: JSON.stringify(row, null, 2) })] })] }));
102
+ }
103
+ //# sourceMappingURL=llm_call_inspector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm_call_inspector.js","sourceRoot":"","sources":["../../../src/components/llm_call_inspector/llm_call_inspector.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * LLMCostDashboard — re-export
3
+ */
4
+ export { LLMCostDashboard } from './llm_cost_dashboard.js';
5
+ export type { LLMCostDashboardProps } from './llm_cost_dashboard.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/llm_cost_dashboard/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * LLMCostDashboard — re-export
3
+ */
4
+ export { LLMCostDashboard } from './llm_cost_dashboard.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/llm_cost_dashboard/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * LLMCostDashboard Component
3
+ *
4
+ * Displays aggregated LLM usage and cost data.
5
+ * Framework-agnostic: accepts a fetch_summary prop instead of accessing
6
+ * the database directly.
7
+ */
8
+ import type { FetchSummaryFn } from '../../lib/observability/types.js';
9
+ export interface LLMCostDashboardProps {
10
+ /** Async function to fetch usage summary data */
11
+ fetch_summary: FetchSummaryFn;
12
+ /** Optional CSS class */
13
+ className?: string;
14
+ }
15
+ export declare function LLMCostDashboard({ fetch_summary, className }: LLMCostDashboardProps): import("react/jsx-runtime").JSX.Element;
16
+ //# sourceMappingURL=llm_cost_dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm_cost_dashboard.d.ts","sourceRoot":"","sources":["../../../src/components/llm_cost_dashboard/llm_cost_dashboard.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,kCAAkC,CAAC;AAM1C,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,aAAa,EAAE,cAAc,CAAC;IAC9B,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgDD,wBAAgB,gBAAgB,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,EAAE,qBAAqB,2CA0UnF"}