hazo_llm_api 1.0.1
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 +536 -0
- package/dist/components/hazo_llm_prompt_config/hazo_llm_prompt_config.d.ts +16 -0
- package/dist/components/hazo_llm_prompt_config/hazo_llm_prompt_config.d.ts.map +1 -0
- package/dist/components/hazo_llm_prompt_config/hazo_llm_prompt_config.js +258 -0
- package/dist/components/hazo_llm_prompt_config/hazo_llm_prompt_config.js.map +1 -0
- package/dist/components/hazo_llm_prompt_config/index.d.ts +8 -0
- package/dist/components/hazo_llm_prompt_config/index.d.ts.map +1 -0
- package/dist/components/hazo_llm_prompt_config/index.js +7 -0
- package/dist/components/hazo_llm_prompt_config/index.js.map +1 -0
- package/dist/components/hazo_llm_prompt_config/types.d.ts +74 -0
- package/dist/components/hazo_llm_prompt_config/types.d.ts.map +1 -0
- package/dist/components/hazo_llm_prompt_config/types.js +8 -0
- package/dist/components/hazo_llm_prompt_config/types.js.map +1 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +7 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/layout/index.d.ts +7 -0
- package/dist/components/layout/index.d.ts.map +1 -0
- package/dist/components/layout/index.js +7 -0
- package/dist/components/layout/index.js.map +1 -0
- package/dist/components/layout/layout.d.ts +21 -0
- package/dist/components/layout/layout.d.ts.map +1 -0
- package/dist/components/layout/layout.js +18 -0
- package/dist/components/layout/layout.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config/config_parser.d.ts +131 -0
- package/dist/lib/config/config_parser.d.ts.map +1 -0
- package/dist/lib/config/config_parser.js +297 -0
- package/dist/lib/config/config_parser.js.map +1 -0
- package/dist/lib/config/index.d.ts +8 -0
- package/dist/lib/config/index.d.ts.map +1 -0
- package/dist/lib/config/index.js +22 -0
- package/dist/lib/config/index.js.map +1 -0
- package/dist/lib/config/provider_loader.d.ts +113 -0
- package/dist/lib/config/provider_loader.d.ts.map +1 -0
- package/dist/lib/config/provider_loader.js +169 -0
- package/dist/lib/config/provider_loader.js.map +1 -0
- package/dist/lib/database/index.d.ts +8 -0
- package/dist/lib/database/index.d.ts.map +1 -0
- package/dist/lib/database/index.js +8 -0
- package/dist/lib/database/index.js.map +1 -0
- package/dist/lib/database/init_database.d.ts +62 -0
- package/dist/lib/database/init_database.d.ts.map +1 -0
- package/dist/lib/database/init_database.js +436 -0
- package/dist/lib/database/init_database.js.map +1 -0
- package/dist/lib/database/utils.d.ts +50 -0
- package/dist/lib/database/utils.d.ts.map +1 -0
- package/dist/lib/database/utils.js +78 -0
- package/dist/lib/database/utils.js.map +1 -0
- package/dist/lib/index.d.ts +14 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +17 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/llm_api/hazo_llm_image_image.d.ts +26 -0
- package/dist/lib/llm_api/hazo_llm_image_image.d.ts.map +1 -0
- package/dist/lib/llm_api/hazo_llm_image_image.js +92 -0
- package/dist/lib/llm_api/hazo_llm_image_image.js.map +1 -0
- package/dist/lib/llm_api/hazo_llm_image_image_text.d.ts +26 -0
- package/dist/lib/llm_api/hazo_llm_image_image_text.d.ts.map +1 -0
- package/dist/lib/llm_api/hazo_llm_image_image_text.js +220 -0
- package/dist/lib/llm_api/hazo_llm_image_image_text.js.map +1 -0
- package/dist/lib/llm_api/hazo_llm_image_text.d.ts +20 -0
- package/dist/lib/llm_api/hazo_llm_image_text.d.ts.map +1 -0
- package/dist/lib/llm_api/hazo_llm_image_text.js +76 -0
- package/dist/lib/llm_api/hazo_llm_image_text.js.map +1 -0
- package/dist/lib/llm_api/hazo_llm_text_image.d.ts +20 -0
- package/dist/lib/llm_api/hazo_llm_text_image.d.ts.map +1 -0
- package/dist/lib/llm_api/hazo_llm_text_image.js +67 -0
- package/dist/lib/llm_api/hazo_llm_text_image.js.map +1 -0
- package/dist/lib/llm_api/hazo_llm_text_image_text.d.ts +26 -0
- package/dist/lib/llm_api/hazo_llm_text_image_text.d.ts.map +1 -0
- package/dist/lib/llm_api/hazo_llm_text_image_text.js +152 -0
- package/dist/lib/llm_api/hazo_llm_text_image_text.js.map +1 -0
- package/dist/lib/llm_api/hazo_llm_text_text.d.ts +20 -0
- package/dist/lib/llm_api/hazo_llm_text_text.d.ts.map +1 -0
- package/dist/lib/llm_api/hazo_llm_text_text.js +89 -0
- package/dist/lib/llm_api/hazo_llm_text_text.js.map +1 -0
- package/dist/lib/llm_api/index.d.ts +99 -0
- package/dist/lib/llm_api/index.d.ts.map +1 -0
- package/dist/lib/llm_api/index.js +952 -0
- package/dist/lib/llm_api/index.js.map +1 -0
- package/dist/lib/llm_api/provider_helper.d.ts +107 -0
- package/dist/lib/llm_api/provider_helper.d.ts.map +1 -0
- package/dist/lib/llm_api/provider_helper.js +159 -0
- package/dist/lib/llm_api/provider_helper.js.map +1 -0
- package/dist/lib/llm_api/types.d.ts +352 -0
- package/dist/lib/llm_api/types.d.ts.map +1 -0
- package/dist/lib/llm_api/types.js +8 -0
- package/dist/lib/llm_api/types.js.map +1 -0
- package/dist/lib/prompts/get_prompt.d.ts +50 -0
- package/dist/lib/prompts/get_prompt.d.ts.map +1 -0
- package/dist/lib/prompts/get_prompt.js +232 -0
- package/dist/lib/prompts/get_prompt.js.map +1 -0
- package/dist/lib/prompts/index.d.ts +9 -0
- package/dist/lib/prompts/index.d.ts.map +1 -0
- package/dist/lib/prompts/index.js +9 -0
- package/dist/lib/prompts/index.js.map +1 -0
- package/dist/lib/prompts/prompt_cache.d.ts +151 -0
- package/dist/lib/prompts/prompt_cache.d.ts.map +1 -0
- package/dist/lib/prompts/prompt_cache.js +276 -0
- package/dist/lib/prompts/prompt_cache.js.map +1 -0
- package/dist/lib/prompts/substitute_variables.d.ts +38 -0
- package/dist/lib/prompts/substitute_variables.d.ts.map +1 -0
- package/dist/lib/prompts/substitute_variables.js +175 -0
- package/dist/lib/prompts/substitute_variables.js.map +1 -0
- package/dist/lib/providers/gemini/gemini_client.d.ts +25 -0
- package/dist/lib/providers/gemini/gemini_client.d.ts.map +1 -0
- package/dist/lib/providers/gemini/gemini_client.js +235 -0
- package/dist/lib/providers/gemini/gemini_client.js.map +1 -0
- package/dist/lib/providers/gemini/gemini_provider.d.ts +111 -0
- package/dist/lib/providers/gemini/gemini_provider.d.ts.map +1 -0
- package/dist/lib/providers/gemini/gemini_provider.js +431 -0
- package/dist/lib/providers/gemini/gemini_provider.js.map +1 -0
- package/dist/lib/providers/gemini/index.d.ts +8 -0
- package/dist/lib/providers/gemini/index.d.ts.map +1 -0
- package/dist/lib/providers/gemini/index.js +8 -0
- package/dist/lib/providers/gemini/index.js.map +1 -0
- package/dist/lib/providers/index.d.ts +8 -0
- package/dist/lib/providers/index.d.ts.map +1 -0
- package/dist/lib/providers/index.js +8 -0
- package/dist/lib/providers/index.js.map +1 -0
- package/dist/lib/providers/qwen/index.d.ts +8 -0
- package/dist/lib/providers/qwen/index.d.ts.map +1 -0
- package/dist/lib/providers/qwen/index.js +8 -0
- package/dist/lib/providers/qwen/index.js.map +1 -0
- package/dist/lib/providers/qwen/qwen_client.d.ts +154 -0
- package/dist/lib/providers/qwen/qwen_client.d.ts.map +1 -0
- package/dist/lib/providers/qwen/qwen_client.js +1002 -0
- package/dist/lib/providers/qwen/qwen_client.js.map +1 -0
- package/dist/lib/providers/qwen/qwen_provider.d.ts +139 -0
- package/dist/lib/providers/qwen/qwen_provider.d.ts.map +1 -0
- package/dist/lib/providers/qwen/qwen_provider.js +304 -0
- package/dist/lib/providers/qwen/qwen_provider.js.map +1 -0
- package/dist/lib/providers/registry.d.ts +66 -0
- package/dist/lib/providers/registry.d.ts.map +1 -0
- package/dist/lib/providers/registry.js +158 -0
- package/dist/lib/providers/registry.js.map +1 -0
- package/dist/lib/providers/types.d.ts +95 -0
- package/dist/lib/providers/types.d.ts.map +1 -0
- package/dist/lib/providers/types.js +19 -0
- package/dist/lib/providers/types.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +32 -0
- package/dist/server.js.map +1 -0
- package/package.json +68 -0
- package/techdoc.md +857 -0
package/techdoc.md
ADDED
|
@@ -0,0 +1,857 @@
|
|
|
1
|
+
# hazo_llm_api - Technical Documentation
|
|
2
|
+
|
|
3
|
+
## Architecture Overview
|
|
4
|
+
|
|
5
|
+
### Package Structure
|
|
6
|
+
|
|
7
|
+
The `hazo_llm_api` package is an ES module package providing:
|
|
8
|
+
- **Multi-Provider LLM System** with centralized registry and provider abstraction
|
|
9
|
+
- **LLM API wrappers** for multiple providers (Gemini, Qwen, extensible to OpenAI, Anthropic, etc.)
|
|
10
|
+
- **Prompt management system** with SQLite storage, LRU caching, and variable substitution
|
|
11
|
+
- **React components** for UI (Layout)
|
|
12
|
+
- **Configuration system** using INI files with environment variable support
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
src/
|
|
16
|
+
├── index.ts # Client entry point (components + types)
|
|
17
|
+
├── server.ts # Server entry point (LLM APIs, database)
|
|
18
|
+
├── components/
|
|
19
|
+
│ └── layout/ # React UI components
|
|
20
|
+
└── lib/
|
|
21
|
+
├── llm_api/ # Core LLM service functions
|
|
22
|
+
│ ├── index.ts # Module exports and initialization
|
|
23
|
+
│ ├── provider_helper.ts # Provider lookup/validation helpers
|
|
24
|
+
│ ├── hazo_llm_*.ts # Service functions (text_text, image_text, etc.)
|
|
25
|
+
│ └── types.ts # Type definitions
|
|
26
|
+
├── providers/ # LLM provider implementations
|
|
27
|
+
│ ├── registry.ts # Provider registration and lookup
|
|
28
|
+
│ ├── types.ts # Provider interface definitions
|
|
29
|
+
│ ├── gemini/ # Gemini provider
|
|
30
|
+
│ └── qwen/ # Qwen provider
|
|
31
|
+
├── config/ # Configuration utilities
|
|
32
|
+
│ ├── config_parser.ts # INI file parsing, generation config
|
|
33
|
+
│ └── provider_loader.ts # Provider factory and loading
|
|
34
|
+
├── database/ # SQLite database layer
|
|
35
|
+
│ ├── init_database.ts # Database initialization and CRUD
|
|
36
|
+
│ └── utils.ts # Shared database utilities
|
|
37
|
+
└── prompts/ # Prompt management
|
|
38
|
+
├── get_prompt.ts # Prompt retrieval
|
|
39
|
+
├── substitute_variables.ts # Variable substitution
|
|
40
|
+
└── prompt_cache.ts # LRU prompt caching
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Entry Points
|
|
44
|
+
|
|
45
|
+
The package has two entry points for proper client/server separation:
|
|
46
|
+
|
|
47
|
+
| Entry Point | Import Path | Purpose |
|
|
48
|
+
|-------------|-------------|---------|
|
|
49
|
+
| Client | `hazo_llm_api` | React components, types (browser-safe) |
|
|
50
|
+
| Server | `hazo_llm_api/server` | LLM APIs, database ops (Node.js only) |
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// Client-side imports
|
|
54
|
+
import { Layout } from 'hazo_llm_api';
|
|
55
|
+
import type { LLMResponse } from 'hazo_llm_api';
|
|
56
|
+
|
|
57
|
+
// Server-side imports
|
|
58
|
+
import { initialize_llm_api, hazo_llm_text_text } from 'hazo_llm_api/server';
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Module System
|
|
62
|
+
|
|
63
|
+
The package uses ES modules with explicit file paths (`.js` extensions) in export statements, as required by ES module bundlers:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// ✅ CORRECT
|
|
67
|
+
export * from './components/index.js';
|
|
68
|
+
|
|
69
|
+
// ❌ WRONG
|
|
70
|
+
export * from './components';
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### TypeScript Configuration
|
|
74
|
+
|
|
75
|
+
- **Package**: Uses `tsconfig.json` for development and `tsconfig.build.json` for production builds
|
|
76
|
+
- **Module Resolution**: `node16` for package builds (not `bundler`)
|
|
77
|
+
- **Module System**: `ESNext` for modern ES module output
|
|
78
|
+
- **Type**: Set to `"module"` in package.json
|
|
79
|
+
|
|
80
|
+
### Build Process
|
|
81
|
+
|
|
82
|
+
1. TypeScript compilation using `tsc`
|
|
83
|
+
2. Source files from `src/` are compiled to `dist/`
|
|
84
|
+
3. Declaration files (`.d.ts`) are generated
|
|
85
|
+
4. Source maps are included for debugging
|
|
86
|
+
|
|
87
|
+
## Test Application Architecture
|
|
88
|
+
|
|
89
|
+
### Technology Stack
|
|
90
|
+
|
|
91
|
+
- **Framework**: Next.js 14 (App Router)
|
|
92
|
+
- **Language**: TypeScript
|
|
93
|
+
- **Styling**: TailwindCSS
|
|
94
|
+
- **UI Components**: Shadcn/UI
|
|
95
|
+
- **Icons**: Lucide React
|
|
96
|
+
|
|
97
|
+
### Project Structure
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
test-app/
|
|
101
|
+
├── app/ # Next.js App Router directory
|
|
102
|
+
│ ├── layout.tsx # Root layout
|
|
103
|
+
│ ├── page.tsx # Home page
|
|
104
|
+
│ └── globals.css # Global styles
|
|
105
|
+
├── components/
|
|
106
|
+
│ └── sidebar.tsx # Sidebar navigation component
|
|
107
|
+
├── lib/
|
|
108
|
+
│ └── utils.ts # Utility functions
|
|
109
|
+
├── next.config.js # Next.js configuration
|
|
110
|
+
├── tsconfig.json # TypeScript configuration
|
|
111
|
+
├── tailwind.config.ts # TailwindCSS configuration
|
|
112
|
+
└── components.json # Shadcn/UI configuration
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Next.js Configuration
|
|
116
|
+
|
|
117
|
+
The test-app uses:
|
|
118
|
+
- React Server Components (RSC) enabled
|
|
119
|
+
- Package transpilation for `hazo_llm_api`
|
|
120
|
+
- App Router structure
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
transpilePackages: ['hazo_llm_api']
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This ensures the local package is properly transpiled by Next.js.
|
|
127
|
+
|
|
128
|
+
### Styling System
|
|
129
|
+
|
|
130
|
+
TailwindCSS with Shadcn/UI theming:
|
|
131
|
+
- CSS variables for theming
|
|
132
|
+
- Dark mode support
|
|
133
|
+
- Custom color palette
|
|
134
|
+
- Responsive design utilities
|
|
135
|
+
|
|
136
|
+
## Component Details
|
|
137
|
+
|
|
138
|
+
### Layout Component
|
|
139
|
+
|
|
140
|
+
**Location**: `src/components/layout/layout.tsx`
|
|
141
|
+
|
|
142
|
+
**Purpose**: Provides a consistent page structure with optional sidebar and header.
|
|
143
|
+
|
|
144
|
+
**Structure**:
|
|
145
|
+
```tsx
|
|
146
|
+
<Layout>
|
|
147
|
+
<Sidebar />
|
|
148
|
+
<Header />
|
|
149
|
+
<MainContent />
|
|
150
|
+
</Layout>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Implementation Details**:
|
|
154
|
+
- Flexbox-based layout
|
|
155
|
+
- Full viewport height
|
|
156
|
+
- Sidebar on the left (optional)
|
|
157
|
+
- Header at the top (optional)
|
|
158
|
+
- Scrollable main content area
|
|
159
|
+
|
|
160
|
+
**CSS Classes**:
|
|
161
|
+
- `cls_layout_container` - Main container
|
|
162
|
+
- `cls_layout_sidebar` - Sidebar wrapper
|
|
163
|
+
- `cls_layout_main` - Main content wrapper
|
|
164
|
+
- `cls_layout_header` - Header wrapper
|
|
165
|
+
- `cls_layout_content` - Content area
|
|
166
|
+
|
|
167
|
+
### Sidebar Component (Test App)
|
|
168
|
+
|
|
169
|
+
**Location**: `test-app/components/sidebar.tsx`
|
|
170
|
+
|
|
171
|
+
**Purpose**: Navigation sidebar for the test application.
|
|
172
|
+
|
|
173
|
+
**Features**:
|
|
174
|
+
- Active route highlighting
|
|
175
|
+
- Icon support via Lucide React
|
|
176
|
+
- Responsive design
|
|
177
|
+
- Client-side navigation
|
|
178
|
+
|
|
179
|
+
## Configuration System
|
|
180
|
+
|
|
181
|
+
### Configuration File
|
|
182
|
+
|
|
183
|
+
**File**: `hazo_llm_api_config.ini`
|
|
184
|
+
|
|
185
|
+
**Format**: INI format with sections
|
|
186
|
+
|
|
187
|
+
**Sections**:
|
|
188
|
+
- `[llm]` - Global LLM configuration (enabled_llms, primary_llm, sqlite_path)
|
|
189
|
+
- `[llm_gemini]` - Gemini provider configuration
|
|
190
|
+
- `[llm_qwen]` - Qwen provider configuration
|
|
191
|
+
- `[llm_<provider>]` - Custom provider configurations
|
|
192
|
+
- `[logging]` - Log file paths and settings
|
|
193
|
+
- `[package]` - Build configuration
|
|
194
|
+
- `[test_app]` - Test app settings
|
|
195
|
+
- `[ui]` - UI defaults
|
|
196
|
+
- `[database]` - Database configuration
|
|
197
|
+
|
|
198
|
+
### Global LLM Configuration
|
|
199
|
+
|
|
200
|
+
The `[llm]` section controls which providers are available:
|
|
201
|
+
|
|
202
|
+
```ini
|
|
203
|
+
[llm]
|
|
204
|
+
# JSON array or comma-separated list of enabled providers
|
|
205
|
+
enabled_llms=["gemini", "qwen"]
|
|
206
|
+
# Default provider when not specified in API calls
|
|
207
|
+
primary_llm=gemini
|
|
208
|
+
# SQLite database path (relative to app root)
|
|
209
|
+
sqlite_path=prompt_library.sqlite
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Provider Configuration Sections
|
|
213
|
+
|
|
214
|
+
Each provider has its own `[llm_<name>]` section:
|
|
215
|
+
|
|
216
|
+
```ini
|
|
217
|
+
[llm_gemini]
|
|
218
|
+
# API endpoints
|
|
219
|
+
api_url=https://generativelanguage.googleapis.com/v1/models/gemini-2.5-flash:generateContent
|
|
220
|
+
api_url_image=https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image:generateContent
|
|
221
|
+
|
|
222
|
+
# Optional per-service model overrides
|
|
223
|
+
model_text_text=gemini-2.5-flash
|
|
224
|
+
model_image_text=gemini-2.5-flash
|
|
225
|
+
model_text_image=gemini-2.5-flash-image
|
|
226
|
+
model_image_image=gemini-2.5-flash-image
|
|
227
|
+
|
|
228
|
+
# Capabilities this provider supports (JSON array)
|
|
229
|
+
capabilities=["text_text", "image_text", "text_image", "image_image"]
|
|
230
|
+
|
|
231
|
+
# Generation parameters (prefixed by service type)
|
|
232
|
+
text_temperature=0.7
|
|
233
|
+
text_maxOutputTokens=1024
|
|
234
|
+
text_topP=0.95
|
|
235
|
+
image_temperature=0.1
|
|
236
|
+
image_topK=20
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Generation Parameter Prefixes
|
|
240
|
+
|
|
241
|
+
Use `text_` or `image_` prefixes to configure parameters per service type:
|
|
242
|
+
|
|
243
|
+
| Parameter | Type | Description | Example |
|
|
244
|
+
|-----------|------|-------------|---------|
|
|
245
|
+
| `temperature` | number | Randomness (0.0-2.0) | `text_temperature=0.7` |
|
|
246
|
+
| `maxOutputTokens` | number | Max response length | `text_maxOutputTokens=1024` |
|
|
247
|
+
| `topP` | number | Nucleus sampling (0.0-1.0) | `text_topP=0.95` |
|
|
248
|
+
| `topK` | number | Top-k sampling | `image_topK=20` |
|
|
249
|
+
| `stopSequences` | JSON array | Stop sequences | `text_stopSequences=["END"]` |
|
|
250
|
+
| `responseMimeType` | string | Response format | `text_responseMimeType=text/plain` |
|
|
251
|
+
|
|
252
|
+
**Qwen-specific parameters:**
|
|
253
|
+
- `max_tokens` instead of `maxOutputTokens`
|
|
254
|
+
- `top_p` instead of `topP`
|
|
255
|
+
- `top_k` instead of `topK`
|
|
256
|
+
- `stop` instead of `stopSequences`
|
|
257
|
+
- `presence_penalty`, `frequency_penalty`, `n`
|
|
258
|
+
|
|
259
|
+
### Environment Variables
|
|
260
|
+
|
|
261
|
+
**File**: `.env.local` (not committed to git)
|
|
262
|
+
|
|
263
|
+
**Usage**: Store sensitive API keys
|
|
264
|
+
|
|
265
|
+
**Format**:
|
|
266
|
+
```bash
|
|
267
|
+
# Provider API keys (uppercase provider name + _API_KEY)
|
|
268
|
+
GEMINI_API_KEY=your_gemini_api_key_here
|
|
269
|
+
QWEN_API_KEY=your_qwen_api_key_here
|
|
270
|
+
OPENAI_API_KEY=your_openai_api_key_here
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Important**:
|
|
274
|
+
- Never commit `.env.local` to git
|
|
275
|
+
- Add to `.gitignore`
|
|
276
|
+
- API key environment variable naming: `<PROVIDER_NAME>_API_KEY`
|
|
277
|
+
|
|
278
|
+
## Development Workflow
|
|
279
|
+
|
|
280
|
+
### Package Development
|
|
281
|
+
|
|
282
|
+
1. Make changes in `src/` directory
|
|
283
|
+
2. Run `npm run build` to compile
|
|
284
|
+
3. Test in test-app with `npm run dev:test-app`
|
|
285
|
+
4. Package uses local symlink in test-app
|
|
286
|
+
|
|
287
|
+
### Test App Development
|
|
288
|
+
|
|
289
|
+
1. Make changes in `test-app/` directory
|
|
290
|
+
2. Changes are hot-reloaded by Next.js
|
|
291
|
+
3. Access at `http://localhost:3000` (default)
|
|
292
|
+
|
|
293
|
+
### Building for Production
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# Build package
|
|
297
|
+
npm run build
|
|
298
|
+
|
|
299
|
+
# Build test-app
|
|
300
|
+
npm run build:test-app
|
|
301
|
+
|
|
302
|
+
# Start test-app
|
|
303
|
+
npm run start:test-app
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Export System
|
|
307
|
+
|
|
308
|
+
### Package Exports
|
|
309
|
+
|
|
310
|
+
The package.json defines exports:
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"exports": {
|
|
315
|
+
".": {
|
|
316
|
+
"import": "./dist/index.js",
|
|
317
|
+
"types": "./dist/index.d.ts"
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Import Patterns
|
|
324
|
+
|
|
325
|
+
Consumers of the package can import:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// Default import
|
|
329
|
+
import { Layout } from 'hazo_llm_api';
|
|
330
|
+
|
|
331
|
+
// Named imports
|
|
332
|
+
import { Layout } from 'hazo_llm_api';
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## File Naming Conventions
|
|
336
|
+
|
|
337
|
+
- **Components**: `snake_case` (e.g., `layout.tsx`)
|
|
338
|
+
- **Directories**: `snake_case`
|
|
339
|
+
- **CSS Classes**: Prefixed with `cls_` for easy identification
|
|
340
|
+
- **Configuration**: `snake_case` for INI keys
|
|
341
|
+
|
|
342
|
+
## Code Standards
|
|
343
|
+
|
|
344
|
+
### Comments
|
|
345
|
+
|
|
346
|
+
- File-level comments describing purpose
|
|
347
|
+
- Function-level comments describing functionality
|
|
348
|
+
- Major section comments for code organization
|
|
349
|
+
|
|
350
|
+
### TypeScript
|
|
351
|
+
|
|
352
|
+
- Strict mode enabled
|
|
353
|
+
- Explicit types preferred
|
|
354
|
+
- React 18+ types used
|
|
355
|
+
|
|
356
|
+
### React
|
|
357
|
+
|
|
358
|
+
- Functional components only
|
|
359
|
+
- Hooks for state management
|
|
360
|
+
- Client components marked with `'use client'`
|
|
361
|
+
|
|
362
|
+
## Dependencies
|
|
363
|
+
|
|
364
|
+
### Package Dependencies
|
|
365
|
+
|
|
366
|
+
- **Peer Dependencies**: React 18+, React DOM 18+
|
|
367
|
+
- **Dev Dependencies**: TypeScript, type definitions
|
|
368
|
+
|
|
369
|
+
### Test App Dependencies
|
|
370
|
+
|
|
371
|
+
- **Runtime**: Next.js, React, React DOM
|
|
372
|
+
- **Styling**: TailwindCSS, PostCSS, Autoprefixer
|
|
373
|
+
- **UI**: Shadcn/UI components, Lucide icons
|
|
374
|
+
- **Utilities**: clsx, tailwind-merge
|
|
375
|
+
|
|
376
|
+
## Logging
|
|
377
|
+
|
|
378
|
+
Logging configuration is stored in `hazo_llm_api_config.ini`:
|
|
379
|
+
|
|
380
|
+
```ini
|
|
381
|
+
[logging]
|
|
382
|
+
logfile=logs/hazo_llm_api.log
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Logs should be:
|
|
386
|
+
- JSON formatted
|
|
387
|
+
- Pretty printed
|
|
388
|
+
- Include filename, line number, message, and relevant data
|
|
389
|
+
- Rotated daily
|
|
390
|
+
|
|
391
|
+
## LLM Provider System
|
|
392
|
+
|
|
393
|
+
### Architecture Overview
|
|
394
|
+
|
|
395
|
+
The provider system uses a **centralized registry** pattern:
|
|
396
|
+
|
|
397
|
+
```
|
|
398
|
+
┌─────────────────────┐
|
|
399
|
+
│ Service Functions │ (hazo_llm_text_text, etc.)
|
|
400
|
+
└──────────┬──────────┘
|
|
401
|
+
│
|
|
402
|
+
v
|
|
403
|
+
┌─────────────────────┐
|
|
404
|
+
│ Provider Registry │ (lookup, validation, capability checking)
|
|
405
|
+
└──────────┬──────────┘
|
|
406
|
+
│
|
|
407
|
+
v
|
|
408
|
+
┌─────────────────────┐
|
|
409
|
+
│ Provider Instance │ (GeminiProvider, QwenProvider, etc.)
|
|
410
|
+
└─────────────────────┘
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**Key components:**
|
|
414
|
+
1. **Registry** (`lib/providers/registry.ts`): Central provider management
|
|
415
|
+
2. **Provider Interface** (`lib/providers/types.ts`): Contract all providers must implement
|
|
416
|
+
3. **Provider Implementations** (`lib/providers/gemini/`, `lib/providers/qwen/`): Specific LLM integrations
|
|
417
|
+
4. **Configuration Loader** (`lib/llm_api/index.ts`): Reads config and initializes providers
|
|
418
|
+
|
|
419
|
+
### Provider Interface
|
|
420
|
+
|
|
421
|
+
All LLM providers implement the `LLMProvider` interface:
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
interface LLMProvider {
|
|
425
|
+
// Identification and capabilities
|
|
426
|
+
get_name(): string;
|
|
427
|
+
get_capabilities(): Set<ServiceType>;
|
|
428
|
+
get_model_for_service(service_type: ServiceType): string | undefined;
|
|
429
|
+
|
|
430
|
+
// Service implementations
|
|
431
|
+
text_text(params: TextTextParams, logger: Logger): Promise<LLMResponse>;
|
|
432
|
+
image_text(params: ImageTextParams, logger: Logger): Promise<LLMResponse>;
|
|
433
|
+
text_image(params: TextImageParams, logger: Logger): Promise<LLMResponse>;
|
|
434
|
+
image_image(params: ImageImageParams, logger: Logger): Promise<LLMResponse>;
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### Service Types
|
|
439
|
+
|
|
440
|
+
| Service | Constant | Description |
|
|
441
|
+
|---------|----------|-------------|
|
|
442
|
+
| `text_text` | `SERVICE_TYPES.TEXT_TEXT` | Text input → Text output |
|
|
443
|
+
| `image_text` | `SERVICE_TYPES.IMAGE_TEXT` | Image input → Text output (analysis) |
|
|
444
|
+
| `text_image` | `SERVICE_TYPES.TEXT_IMAGE` | Text input → Image output (generation) |
|
|
445
|
+
| `image_image` | `SERVICE_TYPES.IMAGE_IMAGE` | Image input → Image output (transformation) |
|
|
446
|
+
|
|
447
|
+
### Provider Registry
|
|
448
|
+
|
|
449
|
+
The registry manages all active providers:
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
// Registration
|
|
453
|
+
register_provider(new GeminiProvider(config));
|
|
454
|
+
register_provider(new QwenProvider(config));
|
|
455
|
+
|
|
456
|
+
// Lookup
|
|
457
|
+
const provider = get_provider('gemini', logger);
|
|
458
|
+
|
|
459
|
+
// Capability validation
|
|
460
|
+
if (validate_capability(provider, 'text_image', logger)) {
|
|
461
|
+
// Provider supports text → image generation
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Check enabled status
|
|
465
|
+
if (is_llm_enabled('gemini')) {
|
|
466
|
+
// Provider is enabled in config
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
**Registry Functions:**
|
|
471
|
+
- `register_provider(provider)` - Add a provider to the registry
|
|
472
|
+
- `get_provider(name, logger)` - Get provider by name (returns primary if name is null)
|
|
473
|
+
- `set_enabled_llms(names)` - Set which providers are enabled
|
|
474
|
+
- `set_primary_llm(name)` - Set the default provider
|
|
475
|
+
- `get_primary_llm()` - Get the default provider name
|
|
476
|
+
- `validate_capability(provider, service_type, logger)` - Check if provider supports a service
|
|
477
|
+
- `clear_registry()` - Clear all providers (for testing)
|
|
478
|
+
|
|
479
|
+
### Adding a New LLM Provider
|
|
480
|
+
|
|
481
|
+
To add a new provider (e.g., OpenAI), follow these steps:
|
|
482
|
+
|
|
483
|
+
#### Step 1: Create Provider Directory
|
|
484
|
+
|
|
485
|
+
```
|
|
486
|
+
src/lib/providers/openai/
|
|
487
|
+
├── index.ts # Exports and provider class
|
|
488
|
+
├── openai_provider.ts # Provider implementation
|
|
489
|
+
└── openai_client.ts # API client (optional)
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
#### Step 2: Implement the Provider
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
// src/lib/providers/openai/openai_provider.ts
|
|
496
|
+
import type { LLMProvider, ServiceType, LLMCapabilities } from '../types.js';
|
|
497
|
+
import type {
|
|
498
|
+
Logger,
|
|
499
|
+
LLMResponse,
|
|
500
|
+
TextTextParams,
|
|
501
|
+
ImageTextParams,
|
|
502
|
+
TextImageParams,
|
|
503
|
+
ImageImageParams,
|
|
504
|
+
} from '../../llm_api/types.js';
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Configuration interface for OpenAI provider
|
|
508
|
+
*/
|
|
509
|
+
export interface OpenAIProviderConfig {
|
|
510
|
+
api_key: string;
|
|
511
|
+
api_url?: string;
|
|
512
|
+
model_text_text?: string;
|
|
513
|
+
model_image_text?: string;
|
|
514
|
+
model_text_image?: string;
|
|
515
|
+
capabilities?: ServiceType[];
|
|
516
|
+
logger: Logger;
|
|
517
|
+
// Add any provider-specific config here
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* OpenAI LLM Provider Implementation
|
|
522
|
+
*/
|
|
523
|
+
export class OpenAIProvider implements LLMProvider {
|
|
524
|
+
private config: OpenAIProviderConfig;
|
|
525
|
+
private capabilities: LLMCapabilities;
|
|
526
|
+
|
|
527
|
+
constructor(config: OpenAIProviderConfig) {
|
|
528
|
+
this.config = config;
|
|
529
|
+
// Default capabilities if not specified
|
|
530
|
+
this.capabilities = new Set(
|
|
531
|
+
config.capabilities || ['text_text', 'image_text']
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
get_name(): string {
|
|
536
|
+
return 'openai';
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
get_capabilities(): LLMCapabilities {
|
|
540
|
+
return this.capabilities;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
get_model_for_service(service_type: ServiceType): string | undefined {
|
|
544
|
+
const model_map: Record<ServiceType, string | undefined> = {
|
|
545
|
+
text_text: this.config.model_text_text || 'gpt-4',
|
|
546
|
+
image_text: this.config.model_image_text || 'gpt-4-vision-preview',
|
|
547
|
+
text_image: this.config.model_text_image || 'dall-e-3',
|
|
548
|
+
image_image: undefined, // Not supported
|
|
549
|
+
};
|
|
550
|
+
return model_map[service_type];
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async text_text(params: TextTextParams, logger: Logger): Promise<LLMResponse> {
|
|
554
|
+
try {
|
|
555
|
+
const model = this.get_model_for_service('text_text');
|
|
556
|
+
const response = await fetch(
|
|
557
|
+
this.config.api_url || 'https://api.openai.com/v1/chat/completions',
|
|
558
|
+
{
|
|
559
|
+
method: 'POST',
|
|
560
|
+
headers: {
|
|
561
|
+
'Authorization': `Bearer ${this.config.api_key}`,
|
|
562
|
+
'Content-Type': 'application/json',
|
|
563
|
+
},
|
|
564
|
+
body: JSON.stringify({
|
|
565
|
+
model,
|
|
566
|
+
messages: [{ role: 'user', content: params.prompt }],
|
|
567
|
+
}),
|
|
568
|
+
}
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
const data = await response.json();
|
|
572
|
+
|
|
573
|
+
if (!response.ok) {
|
|
574
|
+
logger.error('OpenAI API error', {
|
|
575
|
+
file: 'openai_provider.ts',
|
|
576
|
+
line: 65,
|
|
577
|
+
data: { error: data.error, status: response.status },
|
|
578
|
+
});
|
|
579
|
+
return {
|
|
580
|
+
success: false,
|
|
581
|
+
error: data.error?.message || 'API call failed',
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return {
|
|
586
|
+
success: true,
|
|
587
|
+
text: data.choices?.[0]?.message?.content,
|
|
588
|
+
raw_response: data,
|
|
589
|
+
};
|
|
590
|
+
} catch (error) {
|
|
591
|
+
const error_message = error instanceof Error ? error.message : String(error);
|
|
592
|
+
logger.error('OpenAI text_text failed', {
|
|
593
|
+
file: 'openai_provider.ts',
|
|
594
|
+
line: 78,
|
|
595
|
+
data: { error: error_message },
|
|
596
|
+
});
|
|
597
|
+
return { success: false, error: error_message };
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async image_text(params: ImageTextParams, logger: Logger): Promise<LLMResponse> {
|
|
602
|
+
// Implement GPT-4 Vision API call
|
|
603
|
+
return { success: false, error: 'image_text not yet implemented' };
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
async text_image(params: TextImageParams, logger: Logger): Promise<LLMResponse> {
|
|
607
|
+
// Implement DALL-E API call if needed
|
|
608
|
+
return { success: false, error: 'text_image not yet implemented' };
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async image_image(params: ImageImageParams, logger: Logger): Promise<LLMResponse> {
|
|
612
|
+
// OpenAI doesn't support image-to-image transformation
|
|
613
|
+
return { success: false, error: 'image_image not supported by OpenAI provider' };
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
#### Step 3: Add Provider Loader to `lib/llm_api/index.ts`
|
|
619
|
+
|
|
620
|
+
Add a loader function similar to `load_gemini_provider_from_config` and `load_qwen_provider_from_config`:
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
// In lib/llm_api/index.ts, add this function:
|
|
624
|
+
|
|
625
|
+
function load_openai_provider_from_config(logger: Logger): OpenAIProvider | null {
|
|
626
|
+
const config_path = find_config_file();
|
|
627
|
+
if (!config_path) {
|
|
628
|
+
logger.warn('Config file not found, cannot load OpenAI provider', {
|
|
629
|
+
file: 'index.ts',
|
|
630
|
+
line: 540,
|
|
631
|
+
});
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
try {
|
|
636
|
+
const config_content = fs.readFileSync(config_path, 'utf-8');
|
|
637
|
+
const config = ini.parse(config_content);
|
|
638
|
+
const openai_section = config.llm_openai || {};
|
|
639
|
+
|
|
640
|
+
// Load API key from environment
|
|
641
|
+
const api_key = load_api_key_from_env('openai');
|
|
642
|
+
if (!api_key) {
|
|
643
|
+
logger.error('OPENAI_API_KEY not found in environment variables', {
|
|
644
|
+
file: 'index.ts',
|
|
645
|
+
line: 552,
|
|
646
|
+
data: { config_path },
|
|
647
|
+
});
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Parse capabilities
|
|
652
|
+
const capabilities = parse_capabilities(openai_section.capabilities);
|
|
653
|
+
|
|
654
|
+
const provider_config: OpenAIProviderConfig = {
|
|
655
|
+
api_key,
|
|
656
|
+
api_url: openai_section.api_url,
|
|
657
|
+
model_text_text: openai_section.model_text_text,
|
|
658
|
+
model_image_text: openai_section.model_image_text,
|
|
659
|
+
model_text_image: openai_section.model_text_image,
|
|
660
|
+
capabilities: capabilities.length > 0 ? capabilities : undefined,
|
|
661
|
+
logger,
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
return new OpenAIProvider(provider_config);
|
|
665
|
+
} catch (error) {
|
|
666
|
+
const error_message = error instanceof Error ? error.message : String(error);
|
|
667
|
+
logger.error('Failed to load OpenAI provider from config', {
|
|
668
|
+
file: 'index.ts',
|
|
669
|
+
line: 570,
|
|
670
|
+
data: { error: error_message, config_path },
|
|
671
|
+
});
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Then add to load_and_register_providers function:
|
|
677
|
+
function load_and_register_providers(logger: Logger): void {
|
|
678
|
+
// ... existing code ...
|
|
679
|
+
|
|
680
|
+
for (const llm_name of global_config.enabled_llms) {
|
|
681
|
+
if (llm_name.toLowerCase() === 'gemini') {
|
|
682
|
+
// ... existing Gemini code ...
|
|
683
|
+
} else if (llm_name.toLowerCase() === 'qwen') {
|
|
684
|
+
// ... existing Qwen code ...
|
|
685
|
+
} else if (llm_name.toLowerCase() === 'openai') {
|
|
686
|
+
const provider = load_openai_provider_from_config(logger);
|
|
687
|
+
if (provider) {
|
|
688
|
+
register_provider(provider);
|
|
689
|
+
logger.info('Registered OpenAI provider', {
|
|
690
|
+
file: 'index.ts',
|
|
691
|
+
line: 595,
|
|
692
|
+
data: {
|
|
693
|
+
capabilities: Array.from(provider.get_capabilities()),
|
|
694
|
+
},
|
|
695
|
+
});
|
|
696
|
+
} else {
|
|
697
|
+
logger.warn('OpenAI provider is enabled in config but failed to load', {
|
|
698
|
+
file: 'index.ts',
|
|
699
|
+
line: 602,
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
#### Step 4: Add to Config File
|
|
708
|
+
|
|
709
|
+
```ini
|
|
710
|
+
[llm]
|
|
711
|
+
enabled_llms=["gemini", "qwen", "openai"]
|
|
712
|
+
primary_llm=gemini
|
|
713
|
+
|
|
714
|
+
[llm_openai]
|
|
715
|
+
api_url=https://api.openai.com/v1/chat/completions
|
|
716
|
+
model_text_text=gpt-4
|
|
717
|
+
model_image_text=gpt-4-vision-preview
|
|
718
|
+
model_text_image=dall-e-3
|
|
719
|
+
capabilities=["text_text", "image_text", "text_image"]
|
|
720
|
+
text_temperature=0.7
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
#### Step 5: Add API Key to Environment
|
|
724
|
+
|
|
725
|
+
```bash
|
|
726
|
+
# .env.local
|
|
727
|
+
OPENAI_API_KEY=your_api_key_here
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
#### Step 6: Export Provider
|
|
731
|
+
|
|
732
|
+
```typescript
|
|
733
|
+
// src/lib/providers/openai/index.ts
|
|
734
|
+
export { OpenAIProvider, type OpenAIProviderConfig } from './openai_provider.js';
|
|
735
|
+
|
|
736
|
+
// src/lib/providers/index.ts
|
|
737
|
+
export * from './openai/index.js';
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
### Provider Configuration Parameters
|
|
741
|
+
|
|
742
|
+
Common configuration parameters supported by the config parser:
|
|
743
|
+
|
|
744
|
+
| Parameter | Type | Description |
|
|
745
|
+
|-----------|------|-------------|
|
|
746
|
+
| `temperature` | number | Controls randomness (0.0-2.0) |
|
|
747
|
+
| `max_tokens` | number | Maximum response tokens |
|
|
748
|
+
| `top_p` | number | Nucleus sampling (0.0-1.0) |
|
|
749
|
+
| `top_k` | number | Top-k sampling |
|
|
750
|
+
| `stop_sequences` | array | Stop generation sequences |
|
|
751
|
+
|
|
752
|
+
Use prefixes for service-specific config: `text_temperature`, `image_temperature`
|
|
753
|
+
|
|
754
|
+
## Prompt Management
|
|
755
|
+
|
|
756
|
+
### Database Schema
|
|
757
|
+
|
|
758
|
+
```sql
|
|
759
|
+
CREATE TABLE prompts_library (
|
|
760
|
+
uuid TEXT PRIMARY KEY,
|
|
761
|
+
prompt_area TEXT NOT NULL,
|
|
762
|
+
prompt_key TEXT NOT NULL,
|
|
763
|
+
prompt_text TEXT NOT NULL,
|
|
764
|
+
prompt_variables TEXT DEFAULT '[]',
|
|
765
|
+
prompt_notes TEXT DEFAULT '',
|
|
766
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
767
|
+
changed_by TEXT DEFAULT NULL
|
|
768
|
+
);
|
|
769
|
+
|
|
770
|
+
CREATE INDEX idx_prompts_area_key ON prompts_library(prompt_area, prompt_key);
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### Variable Substitution
|
|
774
|
+
|
|
775
|
+
Use `$variable_name` syntax in prompts:
|
|
776
|
+
|
|
777
|
+
```typescript
|
|
778
|
+
// Prompt text: "Hello $name, your order #$order_id is ready."
|
|
779
|
+
const response = await hazo_llm_text_text({
|
|
780
|
+
prompt_area: 'notifications',
|
|
781
|
+
prompt_key: 'order_ready',
|
|
782
|
+
prompt_variables: {
|
|
783
|
+
name: 'John',
|
|
784
|
+
order_id: '12345',
|
|
785
|
+
},
|
|
786
|
+
});
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
### Prompt Caching
|
|
790
|
+
|
|
791
|
+
The `PromptCache` class implements an LRU (Least Recently Used) cache with TTL support:
|
|
792
|
+
|
|
793
|
+
**Features:**
|
|
794
|
+
- Time-based expiration (TTL)
|
|
795
|
+
- LRU eviction when cache is full
|
|
796
|
+
- Per-entry access tracking
|
|
797
|
+
- Cache statistics (hits, misses, hit rate)
|
|
798
|
+
- Configurable size and TTL
|
|
799
|
+
|
|
800
|
+
**Usage:**
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
import { PromptCache } from 'hazo_llm_api/server';
|
|
804
|
+
|
|
805
|
+
// Create cache instance
|
|
806
|
+
const cache = new PromptCache({
|
|
807
|
+
ttl_ms: 300000, // 5 minutes
|
|
808
|
+
max_size: 100, // Maximum entries
|
|
809
|
+
enabled: true, // Enable/disable caching
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
// Get from cache
|
|
813
|
+
const prompt = cache.get('marketing', 'greeting');
|
|
814
|
+
if (prompt) {
|
|
815
|
+
// Cache hit - use cached prompt
|
|
816
|
+
} else {
|
|
817
|
+
// Cache miss - fetch from database and cache
|
|
818
|
+
const db_prompt = await get_prompt_by_area_and_key(...);
|
|
819
|
+
if (db_prompt) {
|
|
820
|
+
cache.set(db_prompt);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Invalidate specific entry after updates
|
|
825
|
+
cache.invalidate('marketing', 'greeting');
|
|
826
|
+
|
|
827
|
+
// Clear entire cache
|
|
828
|
+
cache.clear();
|
|
829
|
+
|
|
830
|
+
// Get cache statistics
|
|
831
|
+
const stats = cache.get_stats();
|
|
832
|
+
console.log(`Hit rate: ${stats.hit_rate}%`);
|
|
833
|
+
console.log(`Cache size: ${stats.size}/${stats.max_size}`);
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
**Cache Key Format:**
|
|
837
|
+
- Format: `{prompt_area}:{prompt_key}`
|
|
838
|
+
- Example: `marketing:greeting`, `notifications:order_ready`
|
|
839
|
+
|
|
840
|
+
**Eviction Policy:**
|
|
841
|
+
- **TTL Expiration**: Entries older than `ttl_ms` are removed
|
|
842
|
+
- **LRU Eviction**: When cache is full, least recently accessed entry is removed
|
|
843
|
+
- **Manual**: Call `invalidate()` or `clear()`
|
|
844
|
+
|
|
845
|
+
**Default Configuration:**
|
|
846
|
+
- TTL: 5 minutes (300,000 ms)
|
|
847
|
+
- Max Size: 100 entries
|
|
848
|
+
- Enabled: true
|
|
849
|
+
|
|
850
|
+
## Future Considerations
|
|
851
|
+
|
|
852
|
+
- Additional LLM providers (Anthropic, Cohere, etc.)
|
|
853
|
+
- Streaming response support
|
|
854
|
+
- Rate limiting and retry logic
|
|
855
|
+
- Response caching
|
|
856
|
+
- Testing infrastructure
|
|
857
|
+
|