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.
- package/README.md +321 -8
- package/config/hazo_llm_api_config.ini +33 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/llm_call_inspector/index.d.ts +6 -0
- package/dist/components/llm_call_inspector/index.d.ts.map +1 -0
- package/dist/components/llm_call_inspector/index.js +5 -0
- package/dist/components/llm_call_inspector/index.js.map +1 -0
- package/dist/components/llm_call_inspector/llm_call_inspector.d.ts +18 -0
- package/dist/components/llm_call_inspector/llm_call_inspector.d.ts.map +1 -0
- package/dist/components/llm_call_inspector/llm_call_inspector.js +103 -0
- package/dist/components/llm_call_inspector/llm_call_inspector.js.map +1 -0
- package/dist/components/llm_cost_dashboard/index.d.ts +6 -0
- package/dist/components/llm_cost_dashboard/index.d.ts.map +1 -0
- package/dist/components/llm_cost_dashboard/index.js +5 -0
- package/dist/components/llm_cost_dashboard/index.js.map +1 -0
- package/dist/components/llm_cost_dashboard/llm_cost_dashboard.d.ts +16 -0
- package/dist/components/llm_cost_dashboard/llm_cost_dashboard.d.ts.map +1 -0
- package/dist/components/llm_cost_dashboard/llm_cost_dashboard.js +154 -0
- package/dist/components/llm_cost_dashboard/llm_cost_dashboard.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/cascade/cascade_runner.d.ts +50 -0
- package/dist/lib/cascade/cascade_runner.d.ts.map +1 -0
- package/dist/lib/cascade/cascade_runner.js +115 -0
- package/dist/lib/cascade/cascade_runner.js.map +1 -0
- package/dist/lib/cascade/index.d.ts +5 -0
- package/dist/lib/cascade/index.d.ts.map +1 -0
- package/dist/lib/cascade/index.js +4 -0
- package/dist/lib/cascade/index.js.map +1 -0
- package/dist/lib/cascade/types.d.ts +35 -0
- package/dist/lib/cascade/types.d.ts.map +1 -0
- package/dist/lib/cascade/types.js +14 -0
- package/dist/lib/cascade/types.js.map +1 -0
- package/dist/lib/cost_cap/cost_cap.d.ts +40 -0
- package/dist/lib/cost_cap/cost_cap.d.ts.map +1 -0
- package/dist/lib/cost_cap/cost_cap.js +150 -0
- package/dist/lib/cost_cap/cost_cap.js.map +1 -0
- package/dist/lib/cost_cap/index.d.ts +3 -0
- package/dist/lib/cost_cap/index.d.ts.map +1 -0
- package/dist/lib/cost_cap/index.js +2 -0
- package/dist/lib/cost_cap/index.js.map +1 -0
- package/dist/lib/database/init_api_log.d.ts +10 -0
- package/dist/lib/database/init_api_log.d.ts.map +1 -0
- package/dist/lib/database/init_api_log.js +91 -0
- package/dist/lib/database/init_api_log.js.map +1 -0
- package/dist/lib/hazo_connect/direct_db_connect.d.ts +11 -7
- package/dist/lib/hazo_connect/direct_db_connect.d.ts.map +1 -1
- package/dist/lib/hazo_connect/direct_db_connect.js +59 -6
- package/dist/lib/hazo_connect/direct_db_connect.js.map +1 -1
- package/dist/lib/hazo_connect/types.d.ts +35 -3
- package/dist/lib/hazo_connect/types.d.ts.map +1 -1
- package/dist/lib/llm_api/embed_cache.d.ts +15 -0
- package/dist/lib/llm_api/embed_cache.d.ts.map +1 -0
- package/dist/lib/llm_api/embed_cache.js +53 -0
- package/dist/lib/llm_api/embed_cache.js.map +1 -0
- package/dist/lib/llm_api/hazo_llm_document_text.d.ts.map +1 -1
- package/dist/lib/llm_api/hazo_llm_document_text.js +56 -14
- package/dist/lib/llm_api/hazo_llm_document_text.js.map +1 -1
- package/dist/lib/llm_api/hazo_llm_dynamic_data_extract.d.ts.map +1 -1
- package/dist/lib/llm_api/hazo_llm_dynamic_data_extract.js +19 -1
- package/dist/lib/llm_api/hazo_llm_dynamic_data_extract.js.map +1 -1
- package/dist/lib/llm_api/hazo_llm_embed.d.ts +10 -0
- package/dist/lib/llm_api/hazo_llm_embed.d.ts.map +1 -0
- package/dist/lib/llm_api/hazo_llm_embed.js +80 -0
- package/dist/lib/llm_api/hazo_llm_embed.js.map +1 -0
- package/dist/lib/llm_api/hazo_llm_image_image.d.ts.map +1 -1
- package/dist/lib/llm_api/hazo_llm_image_image.js +56 -14
- package/dist/lib/llm_api/hazo_llm_image_image.js.map +1 -1
- package/dist/lib/llm_api/hazo_llm_image_text.d.ts.map +1 -1
- package/dist/lib/llm_api/hazo_llm_image_text.js +56 -14
- package/dist/lib/llm_api/hazo_llm_image_text.js.map +1 -1
- package/dist/lib/llm_api/hazo_llm_prompt_chain.d.ts.map +1 -1
- package/dist/lib/llm_api/hazo_llm_prompt_chain.js +17 -1
- package/dist/lib/llm_api/hazo_llm_prompt_chain.js.map +1 -1
- package/dist/lib/llm_api/hazo_llm_text_image.d.ts.map +1 -1
- package/dist/lib/llm_api/hazo_llm_text_image.js +56 -14
- package/dist/lib/llm_api/hazo_llm_text_image.js.map +1 -1
- package/dist/lib/llm_api/hazo_llm_text_text.d.ts.map +1 -1
- package/dist/lib/llm_api/hazo_llm_text_text.js +90 -15
- package/dist/lib/llm_api/hazo_llm_text_text.js.map +1 -1
- package/dist/lib/llm_api/index.d.ts +29 -1
- package/dist/lib/llm_api/index.d.ts.map +1 -1
- package/dist/lib/llm_api/index.js +433 -6
- package/dist/lib/llm_api/index.js.map +1 -1
- package/dist/lib/llm_api/prompt_parts_helper.d.ts +15 -0
- package/dist/lib/llm_api/prompt_parts_helper.d.ts.map +1 -0
- package/dist/lib/llm_api/prompt_parts_helper.js +9 -0
- package/dist/lib/llm_api/prompt_parts_helper.js.map +1 -0
- package/dist/lib/llm_api/types.d.ts +187 -2
- package/dist/lib/llm_api/types.d.ts.map +1 -1
- package/dist/lib/llm_api/types.js +4 -0
- package/dist/lib/llm_api/types.js.map +1 -1
- package/dist/lib/maintenance/purge_log_job.d.ts +23 -0
- package/dist/lib/maintenance/purge_log_job.d.ts.map +1 -0
- package/dist/lib/maintenance/purge_log_job.js +42 -0
- package/dist/lib/maintenance/purge_log_job.js.map +1 -0
- package/dist/lib/observability/log_context.d.ts +15 -0
- package/dist/lib/observability/log_context.d.ts.map +1 -0
- package/dist/lib/observability/log_context.js +32 -0
- package/dist/lib/observability/log_context.js.map +1 -0
- package/dist/lib/observability/log_writer.d.ts +35 -0
- package/dist/lib/observability/log_writer.d.ts.map +1 -0
- package/dist/lib/observability/log_writer.js +106 -0
- package/dist/lib/observability/log_writer.js.map +1 -0
- package/dist/lib/observability/queries.d.ts +15 -0
- package/dist/lib/observability/queries.d.ts.map +1 -0
- package/dist/lib/observability/queries.js +78 -0
- package/dist/lib/observability/queries.js.map +1 -0
- package/dist/lib/observability/types.d.ts +77 -0
- package/dist/lib/observability/types.d.ts.map +1 -0
- package/dist/lib/observability/types.js +8 -0
- package/dist/lib/observability/types.js.map +1 -0
- package/dist/lib/pricing/pricing.d.ts +49 -0
- package/dist/lib/pricing/pricing.d.ts.map +1 -0
- package/dist/lib/pricing/pricing.js +153 -0
- package/dist/lib/pricing/pricing.js.map +1 -0
- package/dist/lib/pricing/pricing.json +75 -0
- package/dist/lib/pricing/types.d.ts +58 -0
- package/dist/lib/pricing/types.d.ts.map +1 -0
- package/dist/lib/pricing/types.js +8 -0
- package/dist/lib/pricing/types.js.map +1 -0
- package/dist/lib/providers/anthropic/anthropic_client.d.ts +71 -0
- package/dist/lib/providers/anthropic/anthropic_client.d.ts.map +1 -0
- package/dist/lib/providers/anthropic/anthropic_client.js +134 -0
- package/dist/lib/providers/anthropic/anthropic_client.js.map +1 -0
- package/dist/lib/providers/anthropic/anthropic_provider.d.ts +60 -0
- package/dist/lib/providers/anthropic/anthropic_provider.d.ts.map +1 -0
- package/dist/lib/providers/anthropic/anthropic_provider.js +273 -0
- package/dist/lib/providers/anthropic/anthropic_provider.js.map +1 -0
- package/dist/lib/providers/anthropic/anthropic_response_to_usage.d.ts +21 -0
- package/dist/lib/providers/anthropic/anthropic_response_to_usage.d.ts.map +1 -0
- package/dist/lib/providers/anthropic/anthropic_response_to_usage.js +46 -0
- package/dist/lib/providers/anthropic/anthropic_response_to_usage.js.map +1 -0
- package/dist/lib/providers/anthropic/index.d.ts +3 -0
- package/dist/lib/providers/anthropic/index.d.ts.map +1 -0
- package/dist/lib/providers/anthropic/index.js +2 -0
- package/dist/lib/providers/anthropic/index.js.map +1 -0
- package/dist/lib/providers/deepseek/deepseek_client.d.ts +55 -0
- package/dist/lib/providers/deepseek/deepseek_client.d.ts.map +1 -0
- package/dist/lib/providers/deepseek/deepseek_client.js +129 -0
- package/dist/lib/providers/deepseek/deepseek_client.js.map +1 -0
- package/dist/lib/providers/deepseek/deepseek_provider.d.ts +50 -0
- package/dist/lib/providers/deepseek/deepseek_provider.d.ts.map +1 -0
- package/dist/lib/providers/deepseek/deepseek_provider.js +147 -0
- package/dist/lib/providers/deepseek/deepseek_provider.js.map +1 -0
- package/dist/lib/providers/deepseek/deepseek_response_to_usage.d.ts +21 -0
- package/dist/lib/providers/deepseek/deepseek_response_to_usage.d.ts.map +1 -0
- package/dist/lib/providers/deepseek/deepseek_response_to_usage.js +40 -0
- package/dist/lib/providers/deepseek/deepseek_response_to_usage.js.map +1 -0
- package/dist/lib/providers/deepseek/index.d.ts +3 -0
- package/dist/lib/providers/deepseek/index.d.ts.map +1 -0
- package/dist/lib/providers/deepseek/index.js +2 -0
- package/dist/lib/providers/deepseek/index.js.map +1 -0
- package/dist/lib/providers/gemini/gemini_provider.d.ts.map +1 -1
- package/dist/lib/providers/gemini/gemini_provider.js +40 -4
- package/dist/lib/providers/gemini/gemini_provider.js.map +1 -1
- package/dist/lib/providers/gemini/gemini_response_to_usage.d.ts +37 -0
- package/dist/lib/providers/gemini/gemini_response_to_usage.d.ts.map +1 -0
- package/dist/lib/providers/gemini/gemini_response_to_usage.js +49 -0
- package/dist/lib/providers/gemini/gemini_response_to_usage.js.map +1 -0
- package/dist/lib/providers/index.d.ts +3 -0
- package/dist/lib/providers/index.d.ts.map +1 -1
- package/dist/lib/providers/index.js +3 -0
- package/dist/lib/providers/index.js.map +1 -1
- package/dist/lib/providers/openai/index.d.ts +3 -0
- package/dist/lib/providers/openai/index.d.ts.map +1 -0
- package/dist/lib/providers/openai/index.js +2 -0
- package/dist/lib/providers/openai/index.js.map +1 -0
- package/dist/lib/providers/openai/openai_client.d.ts +99 -0
- package/dist/lib/providers/openai/openai_client.d.ts.map +1 -0
- package/dist/lib/providers/openai/openai_client.js +187 -0
- package/dist/lib/providers/openai/openai_client.js.map +1 -0
- package/dist/lib/providers/openai/openai_provider.d.ts +66 -0
- package/dist/lib/providers/openai/openai_provider.d.ts.map +1 -0
- package/dist/lib/providers/openai/openai_provider.js +297 -0
- package/dist/lib/providers/openai/openai_provider.js.map +1 -0
- package/dist/lib/providers/openai/openai_response_to_usage.d.ts +21 -0
- package/dist/lib/providers/openai/openai_response_to_usage.d.ts.map +1 -0
- package/dist/lib/providers/openai/openai_response_to_usage.js +50 -0
- package/dist/lib/providers/openai/openai_response_to_usage.js.map +1 -0
- package/dist/lib/providers/qwen/qwen_provider.d.ts.map +1 -1
- package/dist/lib/providers/qwen/qwen_provider.js +52 -5
- package/dist/lib/providers/qwen/qwen_provider.js.map +1 -1
- package/dist/lib/providers/qwen/qwen_response_to_usage.d.ts +36 -0
- package/dist/lib/providers/qwen/qwen_response_to_usage.d.ts.map +1 -0
- package/dist/lib/providers/qwen/qwen_response_to_usage.js +50 -0
- package/dist/lib/providers/qwen/qwen_response_to_usage.js.map +1 -0
- package/dist/lib/providers/types.d.ts +16 -6
- package/dist/lib/providers/types.d.ts.map +1 -1
- package/dist/lib/providers/types.js +1 -0
- package/dist/lib/providers/types.js.map +1 -1
- package/dist/lib/utils.d.ts +13 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +16 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/server.d.ts +16 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +29 -2
- package/dist/server.js.map +1 -1
- package/migrations/hazo_llm_api_log.sql +69 -0
- 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
|
|
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
|
|
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 |
|
|
696
|
-
|
|
697
|
-
| Gemini | text_text, image_text, text_image, image_image |
|
|
698
|
-
| Qwen | text_text, image_text, text_image, image_image |
|
|
699
|
-
|
|
|
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
|
|
@@ -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"}
|
package/dist/components/index.js
CHANGED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|