hazo_llm_api 1.0.4 → 1.0.6

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 (145) hide show
  1. package/dist/components/index.d.ts +7 -0
  2. package/dist/components/index.d.ts.map +1 -0
  3. package/dist/components/index.js +7 -0
  4. package/dist/components/index.js.map +1 -0
  5. package/dist/components/layout/index.d.ts +7 -0
  6. package/dist/components/layout/index.d.ts.map +1 -0
  7. package/dist/components/layout/index.js +7 -0
  8. package/dist/components/layout/index.js.map +1 -0
  9. package/dist/components/layout/layout.d.ts +21 -0
  10. package/dist/components/layout/layout.d.ts.map +1 -0
  11. package/dist/components/layout/layout.js +18 -0
  12. package/dist/components/layout/layout.js.map +1 -0
  13. package/dist/index.d.ts +15 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +21 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/lib/config/config_parser.d.ts +131 -0
  18. package/dist/lib/config/config_parser.d.ts.map +1 -0
  19. package/dist/lib/config/config_parser.js +297 -0
  20. package/dist/lib/config/config_parser.js.map +1 -0
  21. package/dist/lib/config/index.d.ts +8 -0
  22. package/dist/lib/config/index.d.ts.map +1 -0
  23. package/dist/lib/config/index.js +22 -0
  24. package/dist/lib/config/index.js.map +1 -0
  25. package/dist/lib/config/provider_loader.d.ts +113 -0
  26. package/dist/lib/config/provider_loader.d.ts.map +1 -0
  27. package/dist/lib/config/provider_loader.js +169 -0
  28. package/dist/lib/config/provider_loader.js.map +1 -0
  29. package/dist/lib/database/index.d.ts +8 -0
  30. package/dist/lib/database/index.d.ts.map +1 -0
  31. package/dist/lib/database/index.js +10 -0
  32. package/dist/lib/database/index.js.map +1 -0
  33. package/dist/lib/database/init_database.d.ts +118 -0
  34. package/dist/lib/database/init_database.d.ts.map +1 -0
  35. package/dist/lib/database/init_database.js +591 -0
  36. package/dist/lib/database/init_database.js.map +1 -0
  37. package/dist/lib/database/utils.d.ts +53 -0
  38. package/dist/lib/database/utils.d.ts.map +1 -0
  39. package/dist/lib/database/utils.js +87 -0
  40. package/dist/lib/database/utils.js.map +1 -0
  41. package/dist/lib/index.d.ts +14 -0
  42. package/dist/lib/index.d.ts.map +1 -0
  43. package/dist/lib/index.js +17 -0
  44. package/dist/lib/index.js.map +1 -0
  45. package/dist/lib/llm_api/chain_helpers.d.ts +117 -0
  46. package/dist/lib/llm_api/chain_helpers.d.ts.map +1 -0
  47. package/dist/lib/llm_api/chain_helpers.js +445 -0
  48. package/dist/lib/llm_api/chain_helpers.js.map +1 -0
  49. package/dist/lib/llm_api/hazo_llm_image_image.d.ts +26 -0
  50. package/dist/lib/llm_api/hazo_llm_image_image.d.ts.map +1 -0
  51. package/dist/lib/llm_api/hazo_llm_image_image.js +94 -0
  52. package/dist/lib/llm_api/hazo_llm_image_image.js.map +1 -0
  53. package/dist/lib/llm_api/hazo_llm_image_image_text.d.ts +26 -0
  54. package/dist/lib/llm_api/hazo_llm_image_image_text.d.ts.map +1 -0
  55. package/dist/lib/llm_api/hazo_llm_image_image_text.js +222 -0
  56. package/dist/lib/llm_api/hazo_llm_image_image_text.js.map +1 -0
  57. package/dist/lib/llm_api/hazo_llm_image_text.d.ts +20 -0
  58. package/dist/lib/llm_api/hazo_llm_image_text.d.ts.map +1 -0
  59. package/dist/lib/llm_api/hazo_llm_image_text.js +78 -0
  60. package/dist/lib/llm_api/hazo_llm_image_text.js.map +1 -0
  61. package/dist/lib/llm_api/hazo_llm_prompt_chain.d.ts +20 -0
  62. package/dist/lib/llm_api/hazo_llm_prompt_chain.d.ts.map +1 -0
  63. package/dist/lib/llm_api/hazo_llm_prompt_chain.js +368 -0
  64. package/dist/lib/llm_api/hazo_llm_prompt_chain.js.map +1 -0
  65. package/dist/lib/llm_api/hazo_llm_text_image.d.ts +20 -0
  66. package/dist/lib/llm_api/hazo_llm_text_image.d.ts.map +1 -0
  67. package/dist/lib/llm_api/hazo_llm_text_image.js +69 -0
  68. package/dist/lib/llm_api/hazo_llm_text_image.js.map +1 -0
  69. package/dist/lib/llm_api/hazo_llm_text_image_text.d.ts +26 -0
  70. package/dist/lib/llm_api/hazo_llm_text_image_text.d.ts.map +1 -0
  71. package/dist/lib/llm_api/hazo_llm_text_image_text.js +154 -0
  72. package/dist/lib/llm_api/hazo_llm_text_image_text.js.map +1 -0
  73. package/dist/lib/llm_api/hazo_llm_text_text.d.ts +20 -0
  74. package/dist/lib/llm_api/hazo_llm_text_text.d.ts.map +1 -0
  75. package/dist/lib/llm_api/hazo_llm_text_text.js +91 -0
  76. package/dist/lib/llm_api/hazo_llm_text_text.js.map +1 -0
  77. package/dist/lib/llm_api/index.d.ts +223 -0
  78. package/dist/lib/llm_api/index.d.ts.map +1 -0
  79. package/dist/lib/llm_api/index.js +1220 -0
  80. package/dist/lib/llm_api/index.js.map +1 -0
  81. package/dist/lib/llm_api/provider_helper.d.ts +163 -0
  82. package/dist/lib/llm_api/provider_helper.d.ts.map +1 -0
  83. package/dist/lib/llm_api/provider_helper.js +346 -0
  84. package/dist/lib/llm_api/provider_helper.js.map +1 -0
  85. package/dist/lib/llm_api/types.d.ts +667 -0
  86. package/dist/lib/llm_api/types.d.ts.map +1 -0
  87. package/dist/lib/llm_api/types.js +49 -0
  88. package/dist/lib/llm_api/types.js.map +1 -0
  89. package/dist/lib/prompts/get_prompt.d.ts +76 -0
  90. package/dist/lib/prompts/get_prompt.d.ts.map +1 -0
  91. package/dist/lib/prompts/get_prompt.js +342 -0
  92. package/dist/lib/prompts/get_prompt.js.map +1 -0
  93. package/dist/lib/prompts/index.d.ts +9 -0
  94. package/dist/lib/prompts/index.d.ts.map +1 -0
  95. package/dist/lib/prompts/index.js +9 -0
  96. package/dist/lib/prompts/index.js.map +1 -0
  97. package/dist/lib/prompts/prompt_cache.d.ts +151 -0
  98. package/dist/lib/prompts/prompt_cache.d.ts.map +1 -0
  99. package/dist/lib/prompts/prompt_cache.js +276 -0
  100. package/dist/lib/prompts/prompt_cache.js.map +1 -0
  101. package/dist/lib/prompts/substitute_variables.d.ts +38 -0
  102. package/dist/lib/prompts/substitute_variables.d.ts.map +1 -0
  103. package/dist/lib/prompts/substitute_variables.js +171 -0
  104. package/dist/lib/prompts/substitute_variables.js.map +1 -0
  105. package/dist/lib/providers/gemini/gemini_client.d.ts +25 -0
  106. package/dist/lib/providers/gemini/gemini_client.d.ts.map +1 -0
  107. package/dist/lib/providers/gemini/gemini_client.js +235 -0
  108. package/dist/lib/providers/gemini/gemini_client.js.map +1 -0
  109. package/dist/lib/providers/gemini/gemini_provider.d.ts +111 -0
  110. package/dist/lib/providers/gemini/gemini_provider.d.ts.map +1 -0
  111. package/dist/lib/providers/gemini/gemini_provider.js +431 -0
  112. package/dist/lib/providers/gemini/gemini_provider.js.map +1 -0
  113. package/dist/lib/providers/gemini/index.d.ts +8 -0
  114. package/dist/lib/providers/gemini/index.d.ts.map +1 -0
  115. package/dist/lib/providers/gemini/index.js +8 -0
  116. package/dist/lib/providers/gemini/index.js.map +1 -0
  117. package/dist/lib/providers/index.d.ts +8 -0
  118. package/dist/lib/providers/index.d.ts.map +1 -0
  119. package/dist/lib/providers/index.js +8 -0
  120. package/dist/lib/providers/index.js.map +1 -0
  121. package/dist/lib/providers/qwen/index.d.ts +8 -0
  122. package/dist/lib/providers/qwen/index.d.ts.map +1 -0
  123. package/dist/lib/providers/qwen/index.js +8 -0
  124. package/dist/lib/providers/qwen/index.js.map +1 -0
  125. package/dist/lib/providers/qwen/qwen_client.d.ts +154 -0
  126. package/dist/lib/providers/qwen/qwen_client.d.ts.map +1 -0
  127. package/dist/lib/providers/qwen/qwen_client.js +1002 -0
  128. package/dist/lib/providers/qwen/qwen_client.js.map +1 -0
  129. package/dist/lib/providers/qwen/qwen_provider.d.ts +139 -0
  130. package/dist/lib/providers/qwen/qwen_provider.d.ts.map +1 -0
  131. package/dist/lib/providers/qwen/qwen_provider.js +304 -0
  132. package/dist/lib/providers/qwen/qwen_provider.js.map +1 -0
  133. package/dist/lib/providers/registry.d.ts +66 -0
  134. package/dist/lib/providers/registry.d.ts.map +1 -0
  135. package/dist/lib/providers/registry.js +158 -0
  136. package/dist/lib/providers/registry.js.map +1 -0
  137. package/dist/lib/providers/types.d.ts +145 -0
  138. package/dist/lib/providers/types.d.ts.map +1 -0
  139. package/dist/lib/providers/types.js +37 -0
  140. package/dist/lib/providers/types.js.map +1 -0
  141. package/dist/server.d.ts +27 -0
  142. package/dist/server.d.ts.map +1 -0
  143. package/dist/server.js +50 -0
  144. package/dist/server.js.map +1 -0
  145. package/package.json +12 -1
@@ -0,0 +1,1220 @@
1
+ /**
2
+ * LLM API Module
3
+ *
4
+ * Main entry point for the LLM API functionality.
5
+ * Provides initialization and specialized LLM functions:
6
+ * - hazo_llm_text_text: Text input → Text output
7
+ * - hazo_llm_image_text: Image input → Text output
8
+ * - hazo_llm_text_image: Text input → Image output
9
+ * - hazo_llm_image_image: Image input → Image output
10
+ * - hazo_llm_text_image_text: Text → Image → Text (chained)
11
+ * - hazo_llm_image_image_text: Images → Image → Text (chained)
12
+ *
13
+ * Database is auto-initialized on module import using config defaults.
14
+ */
15
+ import { LLM_ERROR_CODES } from './types.js';
16
+ import { initialize_database, get_database } from '../database/init_database.js';
17
+ import { hazo_llm_text_text as hazo_llm_text_text_internal } from './hazo_llm_text_text.js';
18
+ import { hazo_llm_image_text as hazo_llm_image_text_internal } from './hazo_llm_image_text.js';
19
+ import { hazo_llm_text_image as hazo_llm_text_image_internal } from './hazo_llm_text_image.js';
20
+ import { hazo_llm_image_image as hazo_llm_image_image_internal } from './hazo_llm_image_image.js';
21
+ import { hazo_llm_text_image_text as hazo_llm_text_image_text_internal } from './hazo_llm_text_image_text.js';
22
+ import { hazo_llm_image_image_text as hazo_llm_image_image_text_internal } from './hazo_llm_image_image_text.js';
23
+ import { hazo_llm_prompt_chain as hazo_llm_prompt_chain_internal } from './hazo_llm_prompt_chain.js';
24
+ import { register_provider, set_enabled_llms, set_primary_llm, get_primary_llm, get_registered_providers, get_provider, } from '../providers/registry.js';
25
+ import { GeminiProvider } from '../providers/gemini/index.js';
26
+ import { QwenProvider } from '../providers/qwen/index.js';
27
+ import { SERVICE_TYPES } from '../providers/types.js';
28
+ import * as fs from 'fs';
29
+ import * as path from 'path';
30
+ import * as ini from 'ini';
31
+ // =============================================================================
32
+ // Module State
33
+ // =============================================================================
34
+ let initialized = false;
35
+ let db_auto_initialized = false;
36
+ let current_config = null;
37
+ // =============================================================================
38
+ // Default Logger
39
+ // =============================================================================
40
+ /**
41
+ * Default console logger used when no custom logger is provided
42
+ * Can be used directly or as a fallback in functions
43
+ */
44
+ export const default_logger = {
45
+ error: (message, meta) => {
46
+ console.error(`[HAZO_LLM_API ERROR] ${message}`, meta ? JSON.stringify(meta, null, 2) : '');
47
+ },
48
+ info: (message, meta) => {
49
+ console.log(`[HAZO_LLM_API INFO] ${message}`, meta ? JSON.stringify(meta, null, 2) : '');
50
+ },
51
+ warn: (message, meta) => {
52
+ console.warn(`[HAZO_LLM_API WARN] ${message}`, meta ? JSON.stringify(meta, null, 2) : '');
53
+ },
54
+ debug: (message, meta) => {
55
+ console.debug(`[HAZO_LLM_API DEBUG] ${message}`, meta ? JSON.stringify(meta, null, 2) : '');
56
+ },
57
+ };
58
+ /**
59
+ * Stored logger instance - set during initialization
60
+ */
61
+ let stored_logger = default_logger;
62
+ /**
63
+ * Stored hooks instance - set during initialization
64
+ */
65
+ let stored_hooks = {};
66
+ /**
67
+ * Get the current logger instance
68
+ * Returns the stored logger (set during initialization) or default logger
69
+ *
70
+ * @returns Current logger instance
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * import { get_logger } from 'hazo_llm_api/server';
75
+ *
76
+ * const logger = get_logger();
77
+ * logger.info('My message');
78
+ * ```
79
+ */
80
+ export function get_logger() {
81
+ return stored_logger;
82
+ }
83
+ /**
84
+ * Set the logger instance
85
+ * Called internally during initialization, but can also be called directly
86
+ *
87
+ * @param logger - Logger instance to use
88
+ */
89
+ export function set_logger(logger) {
90
+ stored_logger = logger;
91
+ }
92
+ /**
93
+ * Get the current hooks configuration
94
+ *
95
+ * @returns Current hooks configuration
96
+ */
97
+ export function get_hooks() {
98
+ return stored_hooks;
99
+ }
100
+ /**
101
+ * Set the hooks configuration
102
+ * Called internally during initialization, but can also be called directly
103
+ *
104
+ * @param hooks - Hooks configuration to use
105
+ */
106
+ export function set_hooks(hooks) {
107
+ stored_hooks = hooks;
108
+ }
109
+ // =============================================================================
110
+ // Config Reader
111
+ // =============================================================================
112
+ /**
113
+ * Find the config file path
114
+ * Searches in config/ subdirectory and parent directories
115
+ * @returns The path to the config file or null if not found
116
+ */
117
+ function find_config_file() {
118
+ const config_filename = 'hazo_llm_api_config.ini';
119
+ // Search paths: config/ in current dir, parent dir, grandparent dir
120
+ const search_paths = [
121
+ path.join(process.cwd(), 'config', config_filename),
122
+ path.join(process.cwd(), '..', 'config', config_filename),
123
+ path.join(process.cwd(), '..', '..', 'config', config_filename),
124
+ ];
125
+ for (const config_path of search_paths) {
126
+ try {
127
+ if (fs.existsSync(config_path)) {
128
+ return config_path;
129
+ }
130
+ }
131
+ catch {
132
+ // Continue to next path
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ /**
138
+ * Read sqlite_path from hazo_llm_api_config.ini file
139
+ * Searches in current directory and parent directories
140
+ */
141
+ function get_sqlite_path_from_config() {
142
+ const default_path = 'prompt_library.sqlite';
143
+ const config_path = find_config_file();
144
+ if (config_path) {
145
+ try {
146
+ const config_content = fs.readFileSync(config_path, 'utf-8');
147
+ const config = ini.parse(config_content);
148
+ const sqlite_path = config.llm?.sqlite_path;
149
+ if (sqlite_path) {
150
+ default_logger.debug('Found sqlite_path in config', {
151
+ file: 'index.ts',
152
+ line: 137,
153
+ data: { config_path, sqlite_path },
154
+ });
155
+ return sqlite_path;
156
+ }
157
+ }
158
+ catch {
159
+ // Use default
160
+ }
161
+ }
162
+ default_logger.debug('Using default sqlite_path', {
163
+ file: 'index.ts',
164
+ line: 150,
165
+ data: { default_path },
166
+ });
167
+ return default_path;
168
+ }
169
+ /**
170
+ * Parse a generation config section from the ini file
171
+ * Only includes parameters that are explicitly set (not commented out)
172
+ * @param section - The parsed ini section object
173
+ * @returns GeminiGenerationConfig or undefined if no params set
174
+ */
175
+ function parse_generation_config(section) {
176
+ if (!section) {
177
+ return undefined;
178
+ }
179
+ const config = {};
180
+ let has_params = false;
181
+ // Parse temperature (number)
182
+ if (section.temperature !== undefined) {
183
+ const temp = parseFloat(section.temperature);
184
+ if (!isNaN(temp)) {
185
+ config.temperature = temp;
186
+ has_params = true;
187
+ }
188
+ }
189
+ // Parse maxOutputTokens / max_output_tokens (number)
190
+ const max_tokens = section.maxOutputTokens || section.max_output_tokens;
191
+ if (max_tokens !== undefined) {
192
+ const tokens = parseInt(max_tokens, 10);
193
+ if (!isNaN(tokens)) {
194
+ config.max_output_tokens = tokens;
195
+ has_params = true;
196
+ }
197
+ }
198
+ // Parse topP / top_p (number)
199
+ const top_p = section.topP || section.top_p;
200
+ if (top_p !== undefined) {
201
+ const p = parseFloat(top_p);
202
+ if (!isNaN(p)) {
203
+ config.top_p = p;
204
+ has_params = true;
205
+ }
206
+ }
207
+ // Parse topK / top_k (number)
208
+ const top_k = section.topK || section.top_k;
209
+ if (top_k !== undefined) {
210
+ const k = parseInt(top_k, 10);
211
+ if (!isNaN(k)) {
212
+ config.top_k = k;
213
+ has_params = true;
214
+ }
215
+ }
216
+ // Parse candidateCount / candidate_count (number)
217
+ const candidate_count = section.candidateCount || section.candidate_count;
218
+ if (candidate_count !== undefined) {
219
+ const count = parseInt(candidate_count, 10);
220
+ if (!isNaN(count)) {
221
+ config.candidate_count = count;
222
+ has_params = true;
223
+ }
224
+ }
225
+ // Parse stopSequences / stop_sequences (JSON array string)
226
+ const stop_sequences = section.stopSequences || section.stop_sequences;
227
+ if (stop_sequences !== undefined) {
228
+ try {
229
+ const sequences = JSON.parse(stop_sequences);
230
+ if (Array.isArray(sequences) && sequences.length > 0) {
231
+ config.stop_sequences = sequences;
232
+ has_params = true;
233
+ }
234
+ }
235
+ catch {
236
+ // Invalid JSON, skip
237
+ }
238
+ }
239
+ // Parse responseMimeType / response_mime_type (string)
240
+ const response_mime_type = section.responseMimeType || section.response_mime_type;
241
+ if (response_mime_type !== undefined) {
242
+ config.response_mime_type = response_mime_type;
243
+ has_params = true;
244
+ }
245
+ return has_params ? config : undefined;
246
+ }
247
+ /**
248
+ * Parse generation config from a section with optional prefix
249
+ * Supports prefixed configs like "text_temperature" or "image_temperature"
250
+ * @param section - The parsed ini section object
251
+ * @param prefix - Optional prefix to filter keys (e.g., "text_" or "image_")
252
+ * @returns GeminiGenerationConfig or undefined if no params set
253
+ */
254
+ function parse_prefixed_generation_config(section, prefix) {
255
+ if (!section) {
256
+ return undefined;
257
+ }
258
+ const config = {};
259
+ let has_params = false;
260
+ // Helper to get value with or without prefix
261
+ const get_value = (key) => {
262
+ if (prefix) {
263
+ return section[`${prefix}${key}`];
264
+ }
265
+ return section[key];
266
+ };
267
+ // Parse temperature (number)
268
+ const temp = get_value('temperature');
269
+ if (temp !== undefined) {
270
+ const temp_val = parseFloat(temp);
271
+ if (!isNaN(temp_val)) {
272
+ config.temperature = temp_val;
273
+ has_params = true;
274
+ }
275
+ }
276
+ // Parse maxOutputTokens (number)
277
+ const max_tokens = get_value('maxOutputTokens') || get_value('max_output_tokens');
278
+ if (max_tokens !== undefined) {
279
+ const tokens = parseInt(max_tokens, 10);
280
+ if (!isNaN(tokens)) {
281
+ config.max_output_tokens = tokens;
282
+ has_params = true;
283
+ }
284
+ }
285
+ // Parse topP (number)
286
+ const top_p = get_value('topP') || get_value('top_p');
287
+ if (top_p !== undefined) {
288
+ const p = parseFloat(top_p);
289
+ if (!isNaN(p)) {
290
+ config.top_p = p;
291
+ has_params = true;
292
+ }
293
+ }
294
+ // Parse topK (number)
295
+ const top_k = get_value('topK') || get_value('top_k');
296
+ if (top_k !== undefined) {
297
+ const k = parseInt(top_k, 10);
298
+ if (!isNaN(k)) {
299
+ config.top_k = k;
300
+ has_params = true;
301
+ }
302
+ }
303
+ // Parse candidateCount (number)
304
+ const candidate_count = get_value('candidateCount') || get_value('candidate_count');
305
+ if (candidate_count !== undefined) {
306
+ const count = parseInt(candidate_count, 10);
307
+ if (!isNaN(count)) {
308
+ config.candidate_count = count;
309
+ has_params = true;
310
+ }
311
+ }
312
+ // Parse stopSequences (JSON array string)
313
+ const stop_sequences = get_value('stopSequences') || get_value('stop_sequences');
314
+ if (stop_sequences !== undefined) {
315
+ try {
316
+ const sequences = JSON.parse(stop_sequences);
317
+ if (Array.isArray(sequences) && sequences.length > 0) {
318
+ config.stop_sequences = sequences;
319
+ has_params = true;
320
+ }
321
+ }
322
+ catch {
323
+ // Invalid JSON, skip
324
+ }
325
+ }
326
+ // Parse responseMimeType (string)
327
+ const response_mime_type = get_value('responseMimeType') || get_value('response_mime_type');
328
+ if (response_mime_type !== undefined) {
329
+ config.response_mime_type = response_mime_type;
330
+ has_params = true;
331
+ }
332
+ return has_params ? config : undefined;
333
+ }
334
+ /**
335
+ * Parse capabilities from config value (JSON array or comma-separated)
336
+ * @param value - Capabilities value from config
337
+ * @returns Array of ServiceType or empty array
338
+ */
339
+ function parse_capabilities(value) {
340
+ if (!value) {
341
+ return [];
342
+ }
343
+ try {
344
+ // Try parsing as JSON array first
345
+ const parsed = JSON.parse(value);
346
+ if (Array.isArray(parsed)) {
347
+ return parsed.filter(cap => Object.values(SERVICE_TYPES).includes(cap));
348
+ }
349
+ }
350
+ catch {
351
+ // Not JSON, try comma-separated
352
+ const caps = value.split(',').map(c => c.trim()).filter(Boolean);
353
+ return caps.filter(cap => Object.values(SERVICE_TYPES).includes(cap));
354
+ }
355
+ return [];
356
+ }
357
+ /**
358
+ * Parse enabled_llms from config (JSON array or comma-separated)
359
+ * @param value - Enabled LLMs value from config
360
+ * @returns Array of LLM names
361
+ */
362
+ function parse_enabled_llms(value) {
363
+ if (!value) {
364
+ return ['gemini']; // Default to gemini
365
+ }
366
+ try {
367
+ // Try parsing as JSON array first
368
+ const parsed = JSON.parse(value);
369
+ if (Array.isArray(parsed)) {
370
+ return parsed.filter((name) => typeof name === 'string' && name.length > 0);
371
+ }
372
+ }
373
+ catch {
374
+ // Not JSON, try comma-separated
375
+ return value.split(',').map(name => name.trim()).filter(Boolean);
376
+ }
377
+ return [];
378
+ }
379
+ /**
380
+ * Load API key from environment variable
381
+ * @param provider_name - Provider name (e.g., "gemini")
382
+ * @returns API key or undefined if not found
383
+ */
384
+ function load_api_key_from_env(provider_name) {
385
+ // Try provider-specific env var: GEMINI_API_KEY, OPENAI_API_KEY, etc.
386
+ const env_var_name = `${provider_name.toUpperCase()}_API_KEY`;
387
+ return process.env[env_var_name];
388
+ }
389
+ /**
390
+ * Read LLM global config from [llm] section
391
+ * @returns Object with enabled_llms and primary_llm
392
+ */
393
+ function get_llm_global_config() {
394
+ const config_path = find_config_file();
395
+ const default_enabled = ['gemini'];
396
+ const default_primary = 'gemini';
397
+ const default_sqlite = 'prompt_library.sqlite';
398
+ if (!config_path) {
399
+ return {
400
+ enabled_llms: default_enabled,
401
+ primary_llm: default_primary,
402
+ sqlite_path: default_sqlite,
403
+ };
404
+ }
405
+ try {
406
+ const config_content = fs.readFileSync(config_path, 'utf-8');
407
+ const config = ini.parse(config_content);
408
+ const llm_section = config.llm || {};
409
+ const enabled_llms = parse_enabled_llms(llm_section.enabled_llms);
410
+ const primary_llm = llm_section.primary_llm || default_primary;
411
+ const sqlite_path = llm_section.sqlite_path || default_sqlite;
412
+ return {
413
+ enabled_llms: enabled_llms.length > 0 ? enabled_llms : default_enabled,
414
+ primary_llm,
415
+ sqlite_path,
416
+ };
417
+ }
418
+ catch {
419
+ return {
420
+ enabled_llms: default_enabled,
421
+ primary_llm: default_primary,
422
+ sqlite_path: default_sqlite,
423
+ };
424
+ }
425
+ }
426
+ /**
427
+ * Load and initialize Gemini provider from config
428
+ * @param logger - Logger instance
429
+ * @returns GeminiProvider instance or null if config invalid
430
+ */
431
+ function load_gemini_provider_from_config(logger) {
432
+ const config_path = find_config_file();
433
+ if (!config_path) {
434
+ logger.warn('Config file not found, cannot load Gemini provider', {
435
+ file: 'index.ts',
436
+ line: 340,
437
+ });
438
+ return null;
439
+ }
440
+ try {
441
+ const config_content = fs.readFileSync(config_path, 'utf-8');
442
+ const config = ini.parse(config_content);
443
+ const gemini_section = config.llm_gemini || {};
444
+ // Support custom env var name via api_key_env config option
445
+ const env_var_name = gemini_section.api_key_env || 'GEMINI_API_KEY';
446
+ const api_key = process.env[env_var_name];
447
+ if (!api_key) {
448
+ logger.error(`${env_var_name} not found in environment variables`, {
449
+ file: 'index.ts',
450
+ line: 352,
451
+ data: { config_path, env_var_name },
452
+ });
453
+ return null;
454
+ }
455
+ // Parse capabilities
456
+ const capabilities = parse_capabilities(gemini_section.capabilities);
457
+ // Parse generation configs with prefixes
458
+ const text_config = parse_prefixed_generation_config(gemini_section, 'text_');
459
+ const image_config = parse_prefixed_generation_config(gemini_section, 'image_');
460
+ const provider_config = {
461
+ api_key,
462
+ api_url: gemini_section.api_url,
463
+ api_url_image: gemini_section.api_url_image,
464
+ model_text_text: gemini_section.model_text_text,
465
+ model_image_text: gemini_section.model_image_text,
466
+ model_text_image: gemini_section.model_text_image,
467
+ model_image_image: gemini_section.model_image_image,
468
+ text_config,
469
+ image_config,
470
+ capabilities: capabilities.length > 0 ? capabilities : undefined,
471
+ logger,
472
+ };
473
+ return new GeminiProvider(provider_config);
474
+ }
475
+ catch (error) {
476
+ const error_message = error instanceof Error ? error.message : String(error);
477
+ logger.error('Failed to load Gemini provider from config', {
478
+ file: 'index.ts',
479
+ line: 378,
480
+ data: { error: error_message, config_path },
481
+ });
482
+ return null;
483
+ }
484
+ }
485
+ /**
486
+ * Parse Qwen generation config from a section with optional prefix
487
+ * Supports prefixed configs like "text_temperature" or "image_temperature"
488
+ * @param section - The parsed ini section object
489
+ * @param prefix - Optional prefix to filter keys (e.g., "text_" or "image_")
490
+ * @returns QwenGenerationConfig or undefined if no params set
491
+ */
492
+ function parse_prefixed_qwen_generation_config(section, prefix) {
493
+ if (!section) {
494
+ return undefined;
495
+ }
496
+ const config = {};
497
+ let has_params = false;
498
+ // Helper to get value with or without prefix
499
+ const get_value = (key) => {
500
+ if (prefix) {
501
+ return section[`${prefix}${key}`];
502
+ }
503
+ return section[key];
504
+ };
505
+ // Parse temperature (number)
506
+ const temp = get_value('temperature');
507
+ if (temp !== undefined) {
508
+ const temp_val = parseFloat(temp);
509
+ if (!isNaN(temp_val)) {
510
+ config.temperature = temp_val;
511
+ has_params = true;
512
+ }
513
+ }
514
+ // Parse max_tokens (number)
515
+ const max_tokens = get_value('max_tokens');
516
+ if (max_tokens !== undefined) {
517
+ const tokens = parseInt(max_tokens, 10);
518
+ if (!isNaN(tokens)) {
519
+ config.max_tokens = tokens;
520
+ has_params = true;
521
+ }
522
+ }
523
+ // Parse top_p (number)
524
+ const top_p = get_value('top_p');
525
+ if (top_p !== undefined) {
526
+ const p = parseFloat(top_p);
527
+ if (!isNaN(p)) {
528
+ config.top_p = p;
529
+ has_params = true;
530
+ }
531
+ }
532
+ // Parse top_k (number)
533
+ const top_k = get_value('top_k');
534
+ if (top_k !== undefined) {
535
+ const k = parseInt(top_k, 10);
536
+ if (!isNaN(k)) {
537
+ config.top_k = k;
538
+ has_params = true;
539
+ }
540
+ }
541
+ // Parse n (number)
542
+ const n = get_value('n');
543
+ if (n !== undefined) {
544
+ const n_val = parseInt(n, 10);
545
+ if (!isNaN(n_val)) {
546
+ config.n = n_val;
547
+ has_params = true;
548
+ }
549
+ }
550
+ // Parse stop (JSON array string)
551
+ const stop = get_value('stop');
552
+ if (stop !== undefined) {
553
+ try {
554
+ const sequences = JSON.parse(stop);
555
+ if (Array.isArray(sequences) && sequences.length > 0) {
556
+ config.stop = sequences;
557
+ has_params = true;
558
+ }
559
+ }
560
+ catch {
561
+ // Invalid JSON, skip
562
+ }
563
+ }
564
+ // Parse presence_penalty (number)
565
+ const presence_penalty = get_value('presence_penalty');
566
+ if (presence_penalty !== undefined) {
567
+ const penalty = parseFloat(presence_penalty);
568
+ if (!isNaN(penalty)) {
569
+ config.presence_penalty = penalty;
570
+ has_params = true;
571
+ }
572
+ }
573
+ // Parse frequency_penalty (number)
574
+ const frequency_penalty = get_value('frequency_penalty');
575
+ if (frequency_penalty !== undefined) {
576
+ const penalty = parseFloat(frequency_penalty);
577
+ if (!isNaN(penalty)) {
578
+ config.frequency_penalty = penalty;
579
+ has_params = true;
580
+ }
581
+ }
582
+ return has_params ? config : undefined;
583
+ }
584
+ /**
585
+ * Load and initialize Qwen provider from config
586
+ * @param logger - Logger instance
587
+ * @returns QwenProvider instance or null if config invalid
588
+ */
589
+ function load_qwen_provider_from_config(logger) {
590
+ const config_path = find_config_file();
591
+ if (!config_path) {
592
+ logger.warn('Config file not found, cannot load Qwen provider', {
593
+ file: 'index.ts',
594
+ line: 500,
595
+ });
596
+ return null;
597
+ }
598
+ try {
599
+ const config_content = fs.readFileSync(config_path, 'utf-8');
600
+ const config = ini.parse(config_content);
601
+ const qwen_section = config.llm_qwen || {};
602
+ // Support custom env var name via api_key_env config option
603
+ const env_var_name = qwen_section.api_key_env || 'QWEN_API_KEY';
604
+ const api_key = process.env[env_var_name];
605
+ if (!api_key) {
606
+ logger.error(`${env_var_name} not found in environment variables`, {
607
+ file: 'index.ts',
608
+ line: 512,
609
+ data: { config_path, env_var_name },
610
+ });
611
+ return null;
612
+ }
613
+ // Parse capabilities
614
+ const capabilities = parse_capabilities(qwen_section.capabilities);
615
+ // Parse generation configs with prefixes
616
+ const text_config = parse_prefixed_qwen_generation_config(qwen_section, 'text_');
617
+ const image_config = parse_prefixed_qwen_generation_config(qwen_section, 'image_');
618
+ const provider_config = {
619
+ api_key,
620
+ api_url: qwen_section.api_url,
621
+ model_text_text: qwen_section.model_text_text,
622
+ model_image_text: qwen_section.model_image_text,
623
+ model_text_image: qwen_section.model_text_image,
624
+ model_image_image: qwen_section.model_image_image,
625
+ api_url_text_text: qwen_section.api_url_text_text,
626
+ api_url_image_text: qwen_section.api_url_image_text,
627
+ api_url_text_image: qwen_section.api_url_text_image,
628
+ api_url_image_image: qwen_section.api_url_image_image,
629
+ system_instruction: qwen_section.system_instruction,
630
+ text_config,
631
+ image_config,
632
+ capabilities: capabilities.length > 0 ? capabilities : undefined,
633
+ logger,
634
+ };
635
+ return new QwenProvider(provider_config);
636
+ }
637
+ catch (error) {
638
+ const error_message = error instanceof Error ? error.message : String(error);
639
+ logger.error('Failed to load Qwen provider from config', {
640
+ file: 'index.ts',
641
+ line: 543,
642
+ data: { error: error_message, config_path },
643
+ });
644
+ return null;
645
+ }
646
+ }
647
+ /**
648
+ * Load and register all enabled providers from config file
649
+ * @param logger - Logger instance
650
+ */
651
+ function load_and_register_providers(logger) {
652
+ const global_config = get_llm_global_config();
653
+ // Set enabled LLMs and primary LLM in registry
654
+ set_enabled_llms(global_config.enabled_llms);
655
+ set_primary_llm(global_config.primary_llm);
656
+ logger.info('Loading LLM providers from config', {
657
+ file: 'index.ts',
658
+ line: 395,
659
+ data: {
660
+ enabled_llms: global_config.enabled_llms,
661
+ primary_llm: global_config.primary_llm,
662
+ },
663
+ });
664
+ // Load each enabled provider
665
+ for (const llm_name of global_config.enabled_llms) {
666
+ if (llm_name.toLowerCase() === 'gemini') {
667
+ const provider = load_gemini_provider_from_config(logger);
668
+ if (provider) {
669
+ register_provider(provider);
670
+ logger.info('Registered Gemini provider', {
671
+ file: 'index.ts',
672
+ line: 636,
673
+ data: {
674
+ capabilities: Array.from(provider.get_capabilities()),
675
+ },
676
+ });
677
+ }
678
+ else {
679
+ logger.warn('Gemini provider is enabled in config but failed to load. Check GEMINI_API_KEY in environment variables.', {
680
+ file: 'index.ts',
681
+ line: 716,
682
+ data: { llm_name: llm_name.toLowerCase() },
683
+ });
684
+ }
685
+ }
686
+ else if (llm_name.toLowerCase() === 'qwen') {
687
+ const provider = load_qwen_provider_from_config(logger);
688
+ if (provider) {
689
+ register_provider(provider);
690
+ logger.info('Registered Qwen provider', {
691
+ file: 'index.ts',
692
+ line: 646,
693
+ data: {
694
+ capabilities: Array.from(provider.get_capabilities()),
695
+ },
696
+ });
697
+ }
698
+ else {
699
+ logger.warn('Qwen provider is enabled in config but failed to load. Check QWEN_API_KEY in environment variables.', {
700
+ file: 'index.ts',
701
+ line: 728,
702
+ data: { llm_name: llm_name.toLowerCase() },
703
+ });
704
+ }
705
+ }
706
+ // Future: Add other providers here (OpenAI, Anthropic, etc.)
707
+ }
708
+ }
709
+ // =============================================================================
710
+ // Auto-initialization on Import
711
+ // =============================================================================
712
+ /**
713
+ * Auto-initialize database when module is imported
714
+ * Uses config file defaults, does not require API key
715
+ */
716
+ async function auto_initialize_database() {
717
+ if (db_auto_initialized) {
718
+ return;
719
+ }
720
+ const file_name = 'index.ts (llm_api)';
721
+ try {
722
+ const sqlite_path = get_sqlite_path_from_config();
723
+ default_logger.info('Auto-initializing database on module import', {
724
+ file: file_name,
725
+ line: 110,
726
+ data: { sqlite_path },
727
+ });
728
+ await initialize_database(sqlite_path, default_logger);
729
+ db_auto_initialized = true;
730
+ default_logger.info('Database auto-initialized successfully', {
731
+ file: file_name,
732
+ line: 118,
733
+ data: { sqlite_path },
734
+ });
735
+ }
736
+ catch (error) {
737
+ const error_message = error instanceof Error ? error.message : String(error);
738
+ default_logger.error('Failed to auto-initialize database', {
739
+ file: file_name,
740
+ line: 124,
741
+ data: { error: error_message },
742
+ });
743
+ // Don't throw - allow manual initialization later
744
+ }
745
+ }
746
+ // Trigger auto-initialization when module is imported
747
+ // Using void to handle the promise without blocking
748
+ void auto_initialize_database();
749
+ // =============================================================================
750
+ // Initialization Function
751
+ // =============================================================================
752
+ /**
753
+ * Initialize the LLM API with the given configuration
754
+ * Creates/connects to the database and prepares the client for use
755
+ *
756
+ * @param config - Configuration options for the LLM API (all fields optional)
757
+ * @returns Initialized LLM API client
758
+ *
759
+ * @example
760
+ * ```typescript
761
+ * // Minimal initialization (uses defaults)
762
+ * const api = await initialize_llm_api({});
763
+ *
764
+ * // With custom logger
765
+ * const api = await initialize_llm_api({ logger: myLogger });
766
+ *
767
+ * // With custom database path
768
+ * const api = await initialize_llm_api({ sqlite_path: '~/data/prompts.db' });
769
+ * ```
770
+ */
771
+ export async function initialize_llm_api(config = {}) {
772
+ const file_name = 'index.ts (llm_api)';
773
+ // Use provided logger or default
774
+ const logger = config.logger || default_logger;
775
+ // Store the logger for use by other functions
776
+ set_logger(logger);
777
+ // Store hooks if provided
778
+ if (config.hooks) {
779
+ set_hooks(config.hooks);
780
+ }
781
+ // Get global config from file
782
+ const global_config = get_llm_global_config();
783
+ // Use provided sqlite_path or fall back to config file value
784
+ const sqlite_path = config.sqlite_path || global_config.sqlite_path;
785
+ // Load and register providers from config file
786
+ load_and_register_providers(logger);
787
+ // Validate that primary_llm is enabled
788
+ const primary_llm_name = get_primary_llm();
789
+ if (!primary_llm_name) {
790
+ const error_msg = 'No primary LLM configured. Set primary_llm in [llm] section of config file.';
791
+ logger.error(error_msg, {
792
+ file: file_name,
793
+ line: 615,
794
+ });
795
+ throw new Error(error_msg);
796
+ }
797
+ logger.info('Initializing LLM API', {
798
+ file: file_name,
799
+ line: 620,
800
+ data: {
801
+ sqlite_path,
802
+ enabled_llms: global_config.enabled_llms,
803
+ primary_llm: primary_llm_name,
804
+ },
805
+ });
806
+ // Set final config
807
+ const final_config = {
808
+ logger,
809
+ sqlite_path,
810
+ hooks: config.hooks,
811
+ };
812
+ // Initialize the database (async)
813
+ try {
814
+ await initialize_database(sqlite_path, logger);
815
+ initialized = true;
816
+ current_config = final_config;
817
+ logger.info('LLM API initialized successfully', {
818
+ file: file_name,
819
+ line: 645,
820
+ data: {
821
+ primary_llm: primary_llm_name,
822
+ registered_providers: get_registered_providers(),
823
+ },
824
+ });
825
+ }
826
+ catch (error) {
827
+ const error_message = error instanceof Error ? error.message : String(error);
828
+ logger.error('Failed to initialize LLM API', {
829
+ file: file_name,
830
+ line: 653,
831
+ data: { error: error_message },
832
+ });
833
+ throw error;
834
+ }
835
+ // Create and return the client instance
836
+ const client = {
837
+ config: final_config,
838
+ db_initialized: initialized,
839
+ hazo_llm_text_text: async (params, llm) => {
840
+ return hazo_llm_text_text(params, llm);
841
+ },
842
+ hazo_llm_image_text: async (params, llm) => {
843
+ return hazo_llm_image_text(params, llm);
844
+ },
845
+ hazo_llm_text_image: async (params, llm) => {
846
+ return hazo_llm_text_image(params, llm);
847
+ },
848
+ hazo_llm_image_image: async (params, llm) => {
849
+ return hazo_llm_image_image(params, llm);
850
+ },
851
+ hazo_llm_text_image_text: async (params, llm) => {
852
+ return hazo_llm_text_image_text(params, llm);
853
+ },
854
+ hazo_llm_image_image_text: async (params, llm) => {
855
+ return hazo_llm_image_image_text(params, llm);
856
+ },
857
+ hazo_llm_prompt_chain: async (params, llm) => {
858
+ return hazo_llm_prompt_chain(params, llm);
859
+ },
860
+ };
861
+ return client;
862
+ }
863
+ // =============================================================================
864
+ // Module Level Functions
865
+ // =============================================================================
866
+ /**
867
+ * Helper to check full LLM API initialization (required for LLM calls)
868
+ * Ensures logger is always present in returned config
869
+ */
870
+ function check_initialized() {
871
+ if (!initialized || !current_config) {
872
+ throw new Error('LLM API not initialized. Call initialize_llm_api first.');
873
+ }
874
+ // Ensure logger is always present
875
+ return {
876
+ ...current_config,
877
+ logger: current_config.logger || get_logger(),
878
+ };
879
+ }
880
+ /**
881
+ * Check if database has been initialized (either auto or manual)
882
+ * @returns true if database is ready for use
883
+ */
884
+ export function is_database_ready() {
885
+ return db_auto_initialized || initialized;
886
+ }
887
+ /**
888
+ * Wait for auto-initialization to complete
889
+ * Useful if you need to ensure database is ready before operations
890
+ */
891
+ export async function ensure_database_ready() {
892
+ if (db_auto_initialized || initialized) {
893
+ return true;
894
+ }
895
+ // If not yet initialized, trigger it now
896
+ await auto_initialize_database();
897
+ return db_auto_initialized;
898
+ }
899
+ /**
900
+ * Text input → Text output
901
+ * Standard text generation using LLM
902
+ *
903
+ * @param params - Text input parameters
904
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified). Use LLM_PROVIDERS constants for type safety.
905
+ * @returns LLM response with generated text
906
+ *
907
+ * @example
908
+ * ```typescript
909
+ * import { hazo_llm_text_text, LLM_PROVIDERS } from 'hazo_llm_api/server';
910
+ *
911
+ * const response = await hazo_llm_text_text({ prompt: 'Hello' }, LLM_PROVIDERS.GEMINI);
912
+ * ```
913
+ */
914
+ export async function hazo_llm_text_text(params, llm) {
915
+ try {
916
+ const config = check_initialized();
917
+ const db = get_database();
918
+ return hazo_llm_text_text_internal(params, db, config, llm);
919
+ }
920
+ catch (error) {
921
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
922
+ }
923
+ }
924
+ /**
925
+ * Image input → Text output
926
+ * Analyze an image and get text description
927
+ *
928
+ * @param params - Image input parameters
929
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified). Use LLM_PROVIDERS constants for type safety.
930
+ * @returns LLM response with text description
931
+ */
932
+ export async function hazo_llm_image_text(params, llm) {
933
+ try {
934
+ const config = check_initialized();
935
+ const db = get_database();
936
+ return hazo_llm_image_text_internal(params, db, config, llm);
937
+ }
938
+ catch (error) {
939
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
940
+ }
941
+ }
942
+ /**
943
+ * Text input → Image output
944
+ * Generate an image from text description
945
+ *
946
+ * @param params - Text input parameters for image generation
947
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified). Use LLM_PROVIDERS constants for type safety.
948
+ * @returns LLM response with generated image
949
+ */
950
+ export async function hazo_llm_text_image(params, llm) {
951
+ try {
952
+ const config = check_initialized();
953
+ const db = get_database();
954
+ return hazo_llm_text_image_internal(params, db, config, llm);
955
+ }
956
+ catch (error) {
957
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
958
+ }
959
+ }
960
+ /**
961
+ * Image input → Image output
962
+ * Transform/edit an image based on instructions
963
+ *
964
+ * @param params - Image input parameters with transformation instructions
965
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified). Use LLM_PROVIDERS constants for type safety.
966
+ * @returns LLM response with transformed image
967
+ */
968
+ export async function hazo_llm_image_image(params, llm) {
969
+ try {
970
+ const config = check_initialized();
971
+ const db = get_database();
972
+ return hazo_llm_image_image_internal(params, db, config, llm);
973
+ }
974
+ catch (error) {
975
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
976
+ }
977
+ }
978
+ /**
979
+ * Text → Image → Text (Chained)
980
+ * Generate an image from prompt_image, then analyze it with prompt_text
981
+ *
982
+ * @param params - Parameters with two prompts: one for image gen, one for analysis
983
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified). Use LLM_PROVIDERS constants for type safety.
984
+ * @returns LLM response with generated image and analysis text
985
+ */
986
+ export async function hazo_llm_text_image_text(params, llm) {
987
+ try {
988
+ const config = check_initialized();
989
+ const db = get_database();
990
+ return hazo_llm_text_image_text_internal(params, db, config, llm);
991
+ }
992
+ catch (error) {
993
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
994
+ }
995
+ }
996
+ /**
997
+ * Images → Image → Text (Chained)
998
+ * Chain multiple image transformations, then describe the final result
999
+ *
1000
+ * @param params - Parameters with images, prompts, and description prompt
1001
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified). Use LLM_PROVIDERS constants for type safety.
1002
+ * @returns LLM response with final image and description text
1003
+ */
1004
+ export async function hazo_llm_image_image_text(params, llm) {
1005
+ try {
1006
+ const config = check_initialized();
1007
+ const db = get_database();
1008
+ return hazo_llm_image_image_text_internal(params, db, config, llm);
1009
+ }
1010
+ catch (error) {
1011
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
1012
+ }
1013
+ }
1014
+ /**
1015
+ * Execute a chain of prompts with dynamic value resolution
1016
+ * Each call can reference values from previous call results
1017
+ *
1018
+ * @param params - Chain parameters including call definitions
1019
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified). Use LLM_PROVIDERS constants for type safety.
1020
+ * @returns Chain response with merged results and individual call outcomes
1021
+ *
1022
+ * @example
1023
+ * ```typescript
1024
+ * import { hazo_llm_prompt_chain } from 'hazo_llm_api/server';
1025
+ *
1026
+ * const response = await hazo_llm_prompt_chain({
1027
+ * chain_calls: [
1028
+ * {
1029
+ * prompt_area: { match_type: 'direct', value: 'document' },
1030
+ * prompt_key: { match_type: 'direct', value: 'initial_read' }
1031
+ * },
1032
+ * {
1033
+ * prompt_area: { match_type: 'direct', value: 'document' },
1034
+ * prompt_key: { match_type: 'direct', value: 'process_results' },
1035
+ * local_1: {
1036
+ * match_type: 'call_chain',
1037
+ * value: 'call[0].tax_category',
1038
+ * variable_name: 'previous_category'
1039
+ * }
1040
+ * }
1041
+ * ]
1042
+ * });
1043
+ * ```
1044
+ */
1045
+ export async function hazo_llm_prompt_chain(params, llm) {
1046
+ try {
1047
+ const config = check_initialized();
1048
+ const db = get_database();
1049
+ return hazo_llm_prompt_chain_internal(params, db, config, llm);
1050
+ }
1051
+ catch (error) {
1052
+ return {
1053
+ success: false,
1054
+ merged_result: {},
1055
+ call_results: [],
1056
+ errors: [{ call_index: -1, error: error instanceof Error ? error.message : String(error) }],
1057
+ total_calls: params.chain_calls.length,
1058
+ successful_calls: 0,
1059
+ };
1060
+ }
1061
+ }
1062
+ // =============================================================================
1063
+ // Streaming Functions
1064
+ // =============================================================================
1065
+ /**
1066
+ * Text input → Text output (Streaming)
1067
+ * Generate text from a prompt with streaming response
1068
+ *
1069
+ * @param params - Text input parameters
1070
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified)
1071
+ * @returns Async generator yielding text chunks
1072
+ *
1073
+ * @example
1074
+ * ```typescript
1075
+ * const stream = await hazo_llm_text_text_stream({ prompt: 'Tell me a story' });
1076
+ *
1077
+ * for await (const chunk of stream) {
1078
+ * if (chunk.error) {
1079
+ * console.error(chunk.error);
1080
+ * break;
1081
+ * }
1082
+ * process.stdout.write(chunk.text);
1083
+ * if (chunk.done) break;
1084
+ * }
1085
+ * ```
1086
+ */
1087
+ export async function* hazo_llm_text_text_stream(params, llm) {
1088
+ try {
1089
+ const config = check_initialized();
1090
+ const logger = config.logger || get_logger();
1091
+ // Get provider
1092
+ const provider = get_provider(llm, logger);
1093
+ if (!provider) {
1094
+ yield {
1095
+ text: '',
1096
+ done: true,
1097
+ error: `Provider "${llm || 'primary'}" not found`,
1098
+ error_info: {
1099
+ code: LLM_ERROR_CODES.PROVIDER_NOT_FOUND,
1100
+ message: `Provider "${llm || 'primary'}" not found`,
1101
+ retryable: false,
1102
+ },
1103
+ };
1104
+ return;
1105
+ }
1106
+ // Check if provider supports streaming
1107
+ if (!provider.text_text_stream) {
1108
+ yield {
1109
+ text: '',
1110
+ done: true,
1111
+ error: `Provider "${provider.get_name()}" does not support streaming`,
1112
+ error_info: {
1113
+ code: LLM_ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
1114
+ message: `Provider "${provider.get_name()}" does not support streaming for text_text`,
1115
+ retryable: false,
1116
+ },
1117
+ };
1118
+ return;
1119
+ }
1120
+ // Call streaming method
1121
+ const stream = await provider.text_text_stream(params, logger);
1122
+ yield* stream;
1123
+ }
1124
+ catch (error) {
1125
+ const error_message = error instanceof Error ? error.message : String(error);
1126
+ yield {
1127
+ text: '',
1128
+ done: true,
1129
+ error: error_message,
1130
+ error_info: {
1131
+ code: LLM_ERROR_CODES.UNKNOWN,
1132
+ message: error_message,
1133
+ retryable: false,
1134
+ },
1135
+ };
1136
+ }
1137
+ }
1138
+ /**
1139
+ * Image input → Text output (Streaming)
1140
+ * Analyze an image and stream text description
1141
+ *
1142
+ * @param params - Image input parameters
1143
+ * @param llm - Optional LLM provider name (uses primary LLM if not specified)
1144
+ * @returns Async generator yielding text chunks
1145
+ */
1146
+ export async function* hazo_llm_image_text_stream(params, llm) {
1147
+ try {
1148
+ const config = check_initialized();
1149
+ const logger = config.logger || get_logger();
1150
+ // Get provider
1151
+ const provider = get_provider(llm, logger);
1152
+ if (!provider) {
1153
+ yield {
1154
+ text: '',
1155
+ done: true,
1156
+ error: `Provider "${llm || 'primary'}" not found`,
1157
+ error_info: {
1158
+ code: LLM_ERROR_CODES.PROVIDER_NOT_FOUND,
1159
+ message: `Provider "${llm || 'primary'}" not found`,
1160
+ retryable: false,
1161
+ },
1162
+ };
1163
+ return;
1164
+ }
1165
+ // Check if provider supports streaming
1166
+ if (!provider.image_text_stream) {
1167
+ yield {
1168
+ text: '',
1169
+ done: true,
1170
+ error: `Provider "${provider.get_name()}" does not support streaming`,
1171
+ error_info: {
1172
+ code: LLM_ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
1173
+ message: `Provider "${provider.get_name()}" does not support streaming for image_text`,
1174
+ retryable: false,
1175
+ },
1176
+ };
1177
+ return;
1178
+ }
1179
+ // Call streaming method
1180
+ const stream = await provider.image_text_stream(params, logger);
1181
+ yield* stream;
1182
+ }
1183
+ catch (error) {
1184
+ const error_message = error instanceof Error ? error.message : String(error);
1185
+ yield {
1186
+ text: '',
1187
+ done: true,
1188
+ error: error_message,
1189
+ error_info: {
1190
+ code: LLM_ERROR_CODES.UNKNOWN,
1191
+ message: error_message,
1192
+ retryable: false,
1193
+ },
1194
+ };
1195
+ }
1196
+ }
1197
+ // =============================================================================
1198
+ // Utility Functions
1199
+ // =============================================================================
1200
+ /**
1201
+ * Check if the LLM API has been initialized
1202
+ * @returns true if initialized
1203
+ */
1204
+ export function is_initialized() {
1205
+ return initialized;
1206
+ }
1207
+ /**
1208
+ * Get the current configuration (without sensitive logger)
1209
+ * @returns Current configuration or null if not initialized
1210
+ */
1211
+ export function get_current_config() {
1212
+ if (!current_config) {
1213
+ return null;
1214
+ }
1215
+ return {
1216
+ sqlite_path: current_config.sqlite_path,
1217
+ hooks: current_config.hooks,
1218
+ };
1219
+ }
1220
+ //# sourceMappingURL=index.js.map