genai-lite 0.6.1 → 0.7.2

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 (38) hide show
  1. package/README.md +21 -0
  2. package/dist/adapters/image/GenaiElectronImageAdapter.js +6 -4
  3. package/dist/adapters/image/OpenAIImageAdapter.js +5 -3
  4. package/dist/config/llm-presets.json +40 -44
  5. package/dist/image/ImageService.d.ts +1 -0
  6. package/dist/image/ImageService.js +13 -10
  7. package/dist/index.d.ts +6 -0
  8. package/dist/index.js +11 -1
  9. package/dist/llm/LLMService.d.ts +6 -0
  10. package/dist/llm/LLMService.js +20 -17
  11. package/dist/llm/clients/AnthropicClientAdapter.js +36 -15
  12. package/dist/llm/clients/GeminiClientAdapter.js +36 -12
  13. package/dist/llm/clients/LlamaCppClientAdapter.js +43 -23
  14. package/dist/llm/clients/MistralClientAdapter.d.ts +94 -0
  15. package/dist/llm/clients/MistralClientAdapter.js +239 -0
  16. package/dist/llm/clients/OpenAIClientAdapter.js +36 -17
  17. package/dist/llm/clients/OpenRouterClientAdapter.d.ts +103 -0
  18. package/dist/llm/clients/OpenRouterClientAdapter.js +285 -0
  19. package/dist/llm/config.js +75 -32
  20. package/dist/llm/services/ModelResolver.d.ts +3 -1
  21. package/dist/llm/services/ModelResolver.js +5 -3
  22. package/dist/llm/services/SettingsManager.d.ts +3 -0
  23. package/dist/llm/services/SettingsManager.js +29 -20
  24. package/dist/llm/types.d.ts +79 -0
  25. package/dist/logging/defaultLogger.d.ts +35 -0
  26. package/dist/logging/defaultLogger.js +94 -0
  27. package/dist/logging/index.d.ts +2 -0
  28. package/dist/logging/index.js +7 -0
  29. package/dist/logging/types.d.ts +23 -0
  30. package/dist/logging/types.js +2 -0
  31. package/dist/prompting/parser.js +4 -1
  32. package/dist/shared/adapters/systemMessageUtils.d.ts +162 -0
  33. package/dist/shared/adapters/systemMessageUtils.js +172 -0
  34. package/dist/shared/services/AdapterRegistry.d.ts +4 -1
  35. package/dist/shared/services/AdapterRegistry.js +12 -9
  36. package/dist/types/image.d.ts +5 -0
  37. package/package.json +2 -1
  38. package/src/config/llm-presets.json +40 -44
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SettingsManager = void 0;
4
+ const defaultLogger_1 = require("../../logging/defaultLogger");
4
5
  const config_1 = require("../config");
5
6
  /**
6
7
  * Manages LLM settings including merging with defaults and filtering unsupported parameters
7
8
  */
8
9
  class SettingsManager {
10
+ constructor(logger) {
11
+ this.logger = logger ?? (0, defaultLogger_1.createDefaultLogger)();
12
+ }
9
13
  /**
10
14
  * Merges request settings with model-specific and global defaults
11
15
  *
@@ -28,6 +32,10 @@ class SettingsManager {
28
32
  user: requestSettings?.user ?? modelDefaults.user,
29
33
  supportsSystemMessage: requestSettings?.supportsSystemMessage ??
30
34
  modelDefaults.supportsSystemMessage,
35
+ systemMessageFallback: {
36
+ ...modelDefaults.systemMessageFallback,
37
+ ...requestSettings?.systemMessageFallback,
38
+ },
31
39
  geminiSafetySettings: requestSettings?.geminiSafetySettings ??
32
40
  modelDefaults.geminiSafetySettings,
33
41
  reasoning: {
@@ -38,9 +46,10 @@ class SettingsManager {
38
46
  ...modelDefaults.thinkingTagFallback,
39
47
  ...requestSettings?.thinkingTagFallback,
40
48
  },
49
+ openRouterProvider: requestSettings?.openRouterProvider ?? modelDefaults.openRouterProvider,
41
50
  };
42
51
  // Log the final settings for debugging
43
- console.log(`Merged settings for ${providerId}/${modelId}:`, {
52
+ this.logger.debug(`Merged settings for ${providerId}/${modelId}:`, {
44
53
  temperature: mergedSettings.temperature,
45
54
  maxTokens: mergedSettings.maxTokens,
46
55
  topP: mergedSettings.topP,
@@ -74,23 +83,23 @@ class SettingsManager {
74
83
  modelInfo.unsupportedParameters.forEach((param) => paramsToExclude.add(param));
75
84
  }
76
85
  if (paramsToExclude.size > 0) {
77
- console.log(`LLMService: Potential parameters to exclude for provider '${providerInfo.id}', model '${modelInfo.id}':`, Array.from(paramsToExclude));
86
+ this.logger.debug(`LLMService: Potential parameters to exclude for provider '${providerInfo.id}', model '${modelInfo.id}':`, Array.from(paramsToExclude));
78
87
  }
79
88
  paramsToExclude.forEach((param) => {
80
89
  // Check if the parameter key actually exists in filteredSettings before trying to delete
81
90
  if (param in filteredSettings) {
82
- console.log(`LLMService: Removing excluded parameter '${String(param)}' for provider '${providerInfo.id}', model '${modelInfo.id}'. Value was:`, filteredSettings[param]);
91
+ this.logger.debug(`LLMService: Removing excluded parameter '${String(param)}' for provider '${providerInfo.id}', model '${modelInfo.id}'. Value was:`, filteredSettings[param]);
83
92
  delete filteredSettings[param]; // Cast to allow deletion
84
93
  }
85
94
  else {
86
95
  // This case should ideally not happen if settings truly is Required<LLMSettings>
87
- console.log(`LLMService: Parameter '${String(param)}' marked for exclusion was not found in settings for provider '${providerInfo.id}', model '${modelInfo.id}'.`);
96
+ this.logger.debug(`LLMService: Parameter '${String(param)}' marked for exclusion was not found in settings for provider '${providerInfo.id}', model '${modelInfo.id}'.`);
88
97
  }
89
98
  });
90
99
  // Handle reasoning settings for models that don't support it
91
100
  // This happens after validateReasoningSettings so we know it's safe to strip
92
101
  if (!modelInfo.reasoning?.supported && filteredSettings.reasoning) {
93
- console.log(`LLMService: Removing reasoning settings for non-reasoning model ${modelInfo.id}`);
102
+ this.logger.debug(`LLMService: Removing reasoning settings for non-reasoning model ${modelInfo.id}`);
94
103
  delete filteredSettings.reasoning;
95
104
  }
96
105
  return filteredSettings;
@@ -121,71 +130,71 @@ class SettingsManager {
121
130
  for (const [key, value] of Object.entries(settings)) {
122
131
  // Check if it's a known field
123
132
  if (!knownFields.includes(key)) {
124
- console.warn(`Unknown setting "${key}" in template metadata. Ignoring.`);
133
+ this.logger.warn(`Unknown setting "${key}" in template metadata. Ignoring.`);
125
134
  continue;
126
135
  }
127
136
  // Type-specific validation
128
137
  if (key === 'temperature') {
129
138
  if (typeof value !== 'number' || value < 0 || value > 2) {
130
- console.warn(`Invalid temperature value in template: ${value}. Must be a number between 0 and 2.`);
139
+ this.logger.warn(`Invalid temperature value in template: ${value}. Must be a number between 0 and 2.`);
131
140
  continue;
132
141
  }
133
142
  }
134
143
  if (key === 'maxTokens') {
135
144
  if (typeof value !== 'number' || value <= 0) {
136
- console.warn(`Invalid maxTokens value in template: ${value}. Must be a positive number.`);
145
+ this.logger.warn(`Invalid maxTokens value in template: ${value}. Must be a positive number.`);
137
146
  continue;
138
147
  }
139
148
  }
140
149
  if (key === 'topP') {
141
150
  if (typeof value !== 'number' || value < 0 || value > 1) {
142
- console.warn(`Invalid topP value in template: ${value}. Must be a number between 0 and 1.`);
151
+ this.logger.warn(`Invalid topP value in template: ${value}. Must be a number between 0 and 1.`);
143
152
  continue;
144
153
  }
145
154
  }
146
155
  if (key === 'stopSequences') {
147
156
  if (!Array.isArray(value) || !value.every(v => typeof v === 'string')) {
148
- console.warn(`Invalid stopSequences value in template. Must be an array of strings.`);
157
+ this.logger.warn(`Invalid stopSequences value in template. Must be an array of strings.`);
149
158
  continue;
150
159
  }
151
160
  }
152
161
  if ((key === 'frequencyPenalty' || key === 'presencePenalty')) {
153
162
  if (typeof value !== 'number' || value < -2 || value > 2) {
154
- console.warn(`Invalid ${key} value in template: ${value}. Must be a number between -2 and 2.`);
163
+ this.logger.warn(`Invalid ${key} value in template: ${value}. Must be a number between -2 and 2.`);
155
164
  continue;
156
165
  }
157
166
  }
158
167
  if (key === 'user' && typeof value !== 'string') {
159
- console.warn(`Invalid user value in template. Must be a string.`);
168
+ this.logger.warn(`Invalid user value in template. Must be a string.`);
160
169
  continue;
161
170
  }
162
171
  if (key === 'supportsSystemMessage' && typeof value !== 'boolean') {
163
- console.warn(`Invalid supportsSystemMessage value in template. Must be a boolean.`);
172
+ this.logger.warn(`Invalid supportsSystemMessage value in template. Must be a boolean.`);
164
173
  continue;
165
174
  }
166
175
  // Nested object validation
167
176
  if (key === 'reasoning' && typeof value === 'object' && value !== null) {
168
177
  const reasoningValidated = {};
169
178
  if ('enabled' in value && typeof value.enabled !== 'boolean') {
170
- console.warn(`Invalid reasoning.enabled value in template. Must be a boolean.`);
179
+ this.logger.warn(`Invalid reasoning.enabled value in template. Must be a boolean.`);
171
180
  }
172
181
  else if ('enabled' in value) {
173
182
  reasoningValidated.enabled = value.enabled;
174
183
  }
175
184
  if ('effort' in value && !['low', 'medium', 'high'].includes(value.effort)) {
176
- console.warn(`Invalid reasoning.effort value in template: ${value.effort}. Must be 'low', 'medium', or 'high'.`);
185
+ this.logger.warn(`Invalid reasoning.effort value in template: ${value.effort}. Must be 'low', 'medium', or 'high'.`);
177
186
  }
178
187
  else if ('effort' in value) {
179
188
  reasoningValidated.effort = value.effort;
180
189
  }
181
190
  if ('maxTokens' in value && (typeof value.maxTokens !== 'number' || value.maxTokens <= 0)) {
182
- console.warn(`Invalid reasoning.maxTokens value in template. Must be a positive number.`);
191
+ this.logger.warn(`Invalid reasoning.maxTokens value in template. Must be a positive number.`);
183
192
  }
184
193
  else if ('maxTokens' in value) {
185
194
  reasoningValidated.maxTokens = value.maxTokens;
186
195
  }
187
196
  if ('exclude' in value && typeof value.exclude !== 'boolean') {
188
- console.warn(`Invalid reasoning.exclude value in template. Must be a boolean.`);
197
+ this.logger.warn(`Invalid reasoning.exclude value in template. Must be a boolean.`);
189
198
  }
190
199
  else if ('exclude' in value) {
191
200
  reasoningValidated.exclude = value.exclude;
@@ -198,19 +207,19 @@ class SettingsManager {
198
207
  if (key === 'thinkingTagFallback' && typeof value === 'object' && value !== null) {
199
208
  const thinkingValidated = {};
200
209
  if ('enabled' in value && typeof value.enabled !== 'boolean') {
201
- console.warn(`Invalid thinkingTagFallback.enabled value in template. Must be a boolean.`);
210
+ this.logger.warn(`Invalid thinkingTagFallback.enabled value in template. Must be a boolean.`);
202
211
  }
203
212
  else if ('enabled' in value) {
204
213
  thinkingValidated.enabled = value.enabled;
205
214
  }
206
215
  if ('tagName' in value && typeof value.tagName !== 'string') {
207
- console.warn(`Invalid thinkingTagFallback.tagName value in template. Must be a string.`);
216
+ this.logger.warn(`Invalid thinkingTagFallback.tagName value in template. Must be a string.`);
208
217
  }
209
218
  else if ('tagName' in value) {
210
219
  thinkingValidated.tagName = value.tagName;
211
220
  }
212
221
  if ('enforce' in value && typeof value.enforce !== 'boolean') {
213
- console.warn(`Invalid thinkingTagFallback.enforce value in template. Must be a boolean.`);
222
+ this.logger.warn(`Invalid thinkingTagFallback.enforce value in template. Must be a boolean.`);
214
223
  }
215
224
  else if ('enforce' in value) {
216
225
  thinkingValidated.enforce = value.enforce;
@@ -83,6 +83,74 @@ export interface LLMThinkingTagFallbackSettings {
83
83
  */
84
84
  enforce?: boolean;
85
85
  }
86
+ /**
87
+ * Format options for prepending system content when model doesn't support system messages.
88
+ * - 'xml': Wrap in XML tags (default) - `<system>content</system>\n\n{user message}`
89
+ * - 'separator': Use a simple separator - `{content}\n\n---\n\n{user message}`
90
+ * - 'plain': Just prepend with double newline - `{content}\n\n{user message}`
91
+ */
92
+ export type SystemMessageFallbackFormat = 'xml' | 'separator' | 'plain';
93
+ /**
94
+ * Settings for handling system messages when the model doesn't support them natively.
95
+ * When a model has `supportsSystemMessage: false`, these settings control how
96
+ * system content is formatted when prepended to the first user message.
97
+ */
98
+ export interface SystemMessageFallbackSettings {
99
+ /**
100
+ * Format to use when prepending system content to user message.
101
+ * @default 'xml'
102
+ */
103
+ format?: SystemMessageFallbackFormat;
104
+ /**
105
+ * Tag name to use when format is 'xml'.
106
+ * @default 'system'
107
+ * @example tagName: 'instructions' produces `<instructions>content</instructions>`
108
+ */
109
+ tagName?: string;
110
+ /**
111
+ * Separator string to use when format is 'separator'.
112
+ * @default '---'
113
+ */
114
+ separator?: string;
115
+ }
116
+ /**
117
+ * OpenRouter-specific provider routing settings
118
+ *
119
+ * These settings allow controlling which underlying providers serve requests
120
+ * when using OpenRouter. All fields are optional - by default, OpenRouter
121
+ * automatically selects the best provider based on price, latency, and availability.
122
+ *
123
+ * @see https://openrouter.ai/docs/provider-routing
124
+ */
125
+ export interface OpenRouterProviderSettings {
126
+ /**
127
+ * Provider priority order. OpenRouter will try providers in this order.
128
+ * @example order: ["Together", "Fireworks", "Lepton"]
129
+ */
130
+ order?: string[];
131
+ /**
132
+ * Providers to exclude from serving this request.
133
+ * @example ignore: ["Azure", "OpenAI"]
134
+ */
135
+ ignore?: string[];
136
+ /**
137
+ * Providers to allow exclusively. If set, only these providers can serve the request.
138
+ * @example allow: ["Together", "Fireworks"]
139
+ */
140
+ allow?: string[];
141
+ /**
142
+ * Control whether providers can use your prompts for training.
143
+ * Set to 'deny' to opt out of data collection by providers.
144
+ * @default undefined (provider's default behavior)
145
+ */
146
+ dataCollection?: 'deny' | 'allow';
147
+ /**
148
+ * If true, only route to providers that support all parameters in your request.
149
+ * Useful when using provider-specific features.
150
+ * @default false
151
+ */
152
+ requireParameters?: boolean;
153
+ }
86
154
  /**
87
155
  * Configurable settings for LLM requests
88
156
  */
@@ -103,6 +171,11 @@ export interface LLMSettings {
103
171
  user?: string;
104
172
  /** Whether the LLM supports system message (almost all LLMs do nowadays) */
105
173
  supportsSystemMessage?: boolean;
174
+ /**
175
+ * Settings for handling system messages when the model doesn't support them.
176
+ * Controls how system content is formatted when prepended to user messages.
177
+ */
178
+ systemMessageFallback?: SystemMessageFallbackSettings;
106
179
  /** Gemini-specific safety settings for content filtering */
107
180
  geminiSafetySettings?: GeminiSafetySetting[];
108
181
  /** Universal reasoning/thinking configuration */
@@ -121,6 +194,12 @@ export interface LLMSettings {
121
194
  * The library only extracts them - it doesn't generate them automatically.
122
195
  */
123
196
  thinkingTagFallback?: LLMThinkingTagFallbackSettings;
197
+ /**
198
+ * OpenRouter-specific provider routing settings.
199
+ * Only used when providerId is 'openrouter'.
200
+ * @see OpenRouterProviderSettings
201
+ */
202
+ openRouterProvider?: OpenRouterProviderSettings;
124
203
  }
125
204
  /**
126
205
  * Request structure for chat completion
@@ -0,0 +1,35 @@
1
+ import type { Logger, LogLevel } from './types';
2
+ /**
3
+ * The default log level, read once at module initialization.
4
+ * Can be overridden per-service via constructor options.
5
+ */
6
+ export declare const DEFAULT_LOG_LEVEL: LogLevel;
7
+ /**
8
+ * Creates a default console-based logger with level filtering.
9
+ *
10
+ * @param level - The minimum log level to output (defaults to env var or 'warn')
11
+ * @returns A Logger instance that filters messages below the specified level
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const logger = createDefaultLogger('debug');
16
+ * logger.debug('This will be shown');
17
+ * logger.info('This will be shown');
18
+ *
19
+ * const quietLogger = createDefaultLogger('error');
20
+ * quietLogger.debug('This will be suppressed');
21
+ * quietLogger.info('This will be suppressed');
22
+ * quietLogger.error('This will be shown');
23
+ * ```
24
+ */
25
+ export declare function createDefaultLogger(level?: LogLevel): Logger;
26
+ /**
27
+ * A silent logger that discards all output.
28
+ * Useful for testing or when logging should be completely disabled.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const service = new LLMService(apiKeyProvider, { logger: silentLogger });
33
+ * ```
34
+ */
35
+ export declare const silentLogger: Logger;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.silentLogger = exports.DEFAULT_LOG_LEVEL = void 0;
4
+ exports.createDefaultLogger = createDefaultLogger;
5
+ /**
6
+ * Numeric values for log levels (higher = more verbose)
7
+ */
8
+ const LOG_LEVEL_VALUES = {
9
+ silent: 0,
10
+ error: 1,
11
+ warn: 2,
12
+ info: 3,
13
+ debug: 4,
14
+ };
15
+ /**
16
+ * Valid log level strings for validation
17
+ */
18
+ const VALID_LOG_LEVELS = new Set(['silent', 'error', 'warn', 'info', 'debug']);
19
+ /**
20
+ * Reads the default log level from environment variable.
21
+ * Called once at module load time.
22
+ *
23
+ * @returns The log level from GENAI_LITE_LOG_LEVEL or 'warn' as default
24
+ */
25
+ function getDefaultLogLevelFromEnv() {
26
+ const envLevel = process.env.GENAI_LITE_LOG_LEVEL?.toLowerCase();
27
+ if (envLevel && VALID_LOG_LEVELS.has(envLevel)) {
28
+ return envLevel;
29
+ }
30
+ return 'warn'; // Sensible default: errors + warnings
31
+ }
32
+ /**
33
+ * The default log level, read once at module initialization.
34
+ * Can be overridden per-service via constructor options.
35
+ */
36
+ exports.DEFAULT_LOG_LEVEL = getDefaultLogLevelFromEnv();
37
+ /**
38
+ * Creates a default console-based logger with level filtering.
39
+ *
40
+ * @param level - The minimum log level to output (defaults to env var or 'warn')
41
+ * @returns A Logger instance that filters messages below the specified level
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const logger = createDefaultLogger('debug');
46
+ * logger.debug('This will be shown');
47
+ * logger.info('This will be shown');
48
+ *
49
+ * const quietLogger = createDefaultLogger('error');
50
+ * quietLogger.debug('This will be suppressed');
51
+ * quietLogger.info('This will be suppressed');
52
+ * quietLogger.error('This will be shown');
53
+ * ```
54
+ */
55
+ function createDefaultLogger(level = exports.DEFAULT_LOG_LEVEL) {
56
+ const threshold = LOG_LEVEL_VALUES[level];
57
+ return {
58
+ debug(message, ...args) {
59
+ if (threshold >= LOG_LEVEL_VALUES.debug) {
60
+ console.log(`[genai-lite:debug] ${message}`, ...args);
61
+ }
62
+ },
63
+ info(message, ...args) {
64
+ if (threshold >= LOG_LEVEL_VALUES.info) {
65
+ console.log(`[genai-lite:info] ${message}`, ...args);
66
+ }
67
+ },
68
+ warn(message, ...args) {
69
+ if (threshold >= LOG_LEVEL_VALUES.warn) {
70
+ console.warn(`[genai-lite:warn] ${message}`, ...args);
71
+ }
72
+ },
73
+ error(message, ...args) {
74
+ if (threshold >= LOG_LEVEL_VALUES.error) {
75
+ console.error(`[genai-lite:error] ${message}`, ...args);
76
+ }
77
+ },
78
+ };
79
+ }
80
+ /**
81
+ * A silent logger that discards all output.
82
+ * Useful for testing or when logging should be completely disabled.
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const service = new LLMService(apiKeyProvider, { logger: silentLogger });
87
+ * ```
88
+ */
89
+ exports.silentLogger = {
90
+ debug: () => { },
91
+ info: () => { },
92
+ warn: () => { },
93
+ error: () => { },
94
+ };
@@ -0,0 +1,2 @@
1
+ export type { Logger, LogLevel, LoggingConfig } from './types';
2
+ export { createDefaultLogger, DEFAULT_LOG_LEVEL, silentLogger, } from './defaultLogger';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.silentLogger = exports.DEFAULT_LOG_LEVEL = exports.createDefaultLogger = void 0;
4
+ var defaultLogger_1 = require("./defaultLogger");
5
+ Object.defineProperty(exports, "createDefaultLogger", { enumerable: true, get: function () { return defaultLogger_1.createDefaultLogger; } });
6
+ Object.defineProperty(exports, "DEFAULT_LOG_LEVEL", { enumerable: true, get: function () { return defaultLogger_1.DEFAULT_LOG_LEVEL; } });
7
+ Object.defineProperty(exports, "silentLogger", { enumerable: true, get: function () { return defaultLogger_1.silentLogger; } });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Log level type - ordered from most quiet to most verbose
3
+ */
4
+ export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';
5
+ /**
6
+ * Logger interface compatible with popular logging libraries
7
+ * (pino, winston, bunyan, console all have these methods)
8
+ */
9
+ export interface Logger {
10
+ debug(message: string, ...args: unknown[]): void;
11
+ info(message: string, ...args: unknown[]): void;
12
+ warn(message: string, ...args: unknown[]): void;
13
+ error(message: string, ...args: unknown[]): void;
14
+ }
15
+ /**
16
+ * Configuration for the logging system
17
+ */
18
+ export interface LoggingConfig {
19
+ /** Log level threshold - messages below this level are suppressed */
20
+ level: LogLevel;
21
+ /** Custom logger implementation (optional) */
22
+ logger?: Logger;
23
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -10,6 +10,9 @@ exports.parseStructuredContent = parseStructuredContent;
10
10
  exports.extractInitialTaggedContent = extractInitialTaggedContent;
11
11
  exports.parseRoleTags = parseRoleTags;
12
12
  exports.parseTemplateWithMetadata = parseTemplateWithMetadata;
13
+ const defaultLogger_1 = require("../logging/defaultLogger");
14
+ // Module-level logger for utility functions
15
+ const logger = (0, defaultLogger_1.createDefaultLogger)();
13
16
  /**
14
17
  * Parses a string containing structured data wrapped in custom XML-style tags.
15
18
  *
@@ -183,7 +186,7 @@ function parseTemplateWithMetadata(template) {
183
186
  catch (error) {
184
187
  // If the JSON is invalid, we warn the developer and treat the <META> block
185
188
  // as regular text to avoid crashing.
186
- console.warn('Could not parse <META> block in template. Treating it as content.', error);
189
+ logger.warn('Could not parse <META> block in template. Treating it as content.', error);
187
190
  return { metadata: { settings: {} }, content: template };
188
191
  }
189
192
  }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Format options for prepending system content when model doesn't support system messages.
3
+ * - 'xml': Wrap in XML tags (default) - `<system>content</system>\n\n{user message}`
4
+ * - 'separator': Use a custom separator string - `{content}{separator}{user message}`
5
+ * Default separator is `\n\n---\n\n`. Specify full separator including whitespace.
6
+ * - 'plain': Just prepend with double newline - `{content}\n\n{user message}`
7
+ */
8
+ export type SystemMessageFallbackFormat = 'xml' | 'separator' | 'plain';
9
+ /**
10
+ * Options for formatting system content when prepending to user messages
11
+ */
12
+ export interface SystemMessageFormatOptions {
13
+ /**
14
+ * Format to use when prepending system content to user message.
15
+ * @default 'xml'
16
+ */
17
+ format?: SystemMessageFallbackFormat;
18
+ /**
19
+ * Tag name to use when format is 'xml'.
20
+ * @default 'system'
21
+ * @example tagName: 'instructions' produces `<instructions>content</instructions>`
22
+ */
23
+ tagName?: string;
24
+ /**
25
+ * Separator string to use when format is 'separator'.
26
+ * Include any whitespace/newlines in the separator itself.
27
+ * @default '\n\n---\n\n'
28
+ * @example separator: '\n\n===\n\n' produces `content\n\n===\n\nuser message`
29
+ */
30
+ separator?: string;
31
+ }
32
+ /**
33
+ * Result of collecting system content for fallback handling
34
+ */
35
+ export interface SystemContentResult {
36
+ /** Combined system content from all sources */
37
+ combinedSystemContent: string | undefined;
38
+ /** Whether to use native system message support */
39
+ useNativeSystemMessage: boolean;
40
+ }
41
+ /**
42
+ * Generic message interface for system message handling
43
+ */
44
+ export interface GenericMessage {
45
+ role: string;
46
+ content: string;
47
+ }
48
+ /**
49
+ * Default format options for system message fallback
50
+ */
51
+ export declare const DEFAULT_SYSTEM_MESSAGE_FORMAT_OPTIONS: Required<SystemMessageFormatOptions>;
52
+ /**
53
+ * Formats system content according to the specified format options.
54
+ *
55
+ * @param systemContent - The system content to format
56
+ * @param userContent - The original user message content
57
+ * @param options - Format options (defaults to XML with 'system' tag)
58
+ * @returns The formatted combined content
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * // XML format (default)
63
+ * formatSystemContentForPrepend('Be helpful', 'Hello', { format: 'xml' });
64
+ * // Returns: '<system>\nBe helpful\n</system>\n\nHello'
65
+ *
66
+ * // Separator format (default separator is '\n\n---\n\n')
67
+ * formatSystemContentForPrepend('Be helpful', 'Hello', { format: 'separator' });
68
+ * // Returns: 'Be helpful\n\n---\n\nHello'
69
+ *
70
+ * // Custom separator (specify full separator including whitespace)
71
+ * formatSystemContentForPrepend('Be helpful', 'Hello', { format: 'separator', separator: '\n\n===\n\n' });
72
+ * // Returns: 'Be helpful\n\n===\n\nHello'
73
+ *
74
+ * // Plain format
75
+ * formatSystemContentForPrepend('Be helpful', 'Hello', { format: 'plain' });
76
+ * // Returns: 'Be helpful\n\nHello'
77
+ * ```
78
+ */
79
+ export declare function formatSystemContentForPrepend(systemContent: string, userContent: string, options?: SystemMessageFormatOptions): string;
80
+ /**
81
+ * Collects and combines system content from multiple sources.
82
+ *
83
+ * Combines the request-level systemMessage with any inline system messages
84
+ * from the messages array into a single string.
85
+ *
86
+ * @param requestSystemMessage - The request.systemMessage field (if any)
87
+ * @param inlineSystemMessages - Array of system message contents from the messages array
88
+ * @param supportsSystemMessage - Whether the model supports native system messages
89
+ * @returns Object with combined content and whether to use native support
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * const { combinedSystemContent, useNativeSystemMessage } = collectSystemContent(
94
+ * 'You are helpful',
95
+ * ['Be concise'],
96
+ * false // Model doesn't support system messages
97
+ * );
98
+ * // combinedSystemContent = 'You are helpful\n\nBe concise'
99
+ * // useNativeSystemMessage = false
100
+ * ```
101
+ */
102
+ export declare function collectSystemContent(requestSystemMessage: string | undefined, inlineSystemMessages: string[], supportsSystemMessage: boolean): SystemContentResult;
103
+ /**
104
+ * Prepends system content to the first user message in an array.
105
+ *
106
+ * This is used as a fallback for models that don't support native system
107
+ * instructions. The system content is formatted according to the options
108
+ * and prepended to the first user message.
109
+ *
110
+ * @param messages - Array of message objects with role and content
111
+ * @param systemContent - System content to prepend
112
+ * @param options - Format options (defaults to XML with 'system' tag)
113
+ * @returns The index of the modified message, or -1 if no user message found
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const messages = [
118
+ * { role: 'user', content: 'Hello' },
119
+ * { role: 'assistant', content: 'Hi!' }
120
+ * ];
121
+ *
122
+ * // Default (XML format)
123
+ * prependSystemToFirstUserMessage(messages, 'Be helpful');
124
+ * // messages[0].content = '<system>\nBe helpful\n</system>\n\nHello'
125
+ *
126
+ * // Plain format
127
+ * prependSystemToFirstUserMessage(messages, 'Be helpful', { format: 'plain' });
128
+ * // messages[0].content = 'Be helpful\n\nHello'
129
+ *
130
+ * // Separator format (default separator is '\n\n---\n\n')
131
+ * prependSystemToFirstUserMessage(messages, 'Be helpful', { format: 'separator' });
132
+ * // messages[0].content = 'Be helpful\n\n---\n\nHello'
133
+ * ```
134
+ */
135
+ export declare function prependSystemToFirstUserMessage<T extends GenericMessage>(messages: T[], systemContent: string, options?: SystemMessageFormatOptions): number;
136
+ /**
137
+ * Filters system messages from an array and returns non-system messages
138
+ * along with collected system content.
139
+ *
140
+ * This is a convenience function that combines message filtering with
141
+ * system content collection in a single pass.
142
+ *
143
+ * @param messages - Array of messages to process
144
+ * @param requestSystemMessage - Optional request-level system message
145
+ * @param supportsSystemMessage - Whether the model supports native system messages
146
+ * @returns Object with filtered messages and system content handling info
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * const messages = [
151
+ * { role: 'system', content: 'Be helpful' },
152
+ * { role: 'user', content: 'Hello' }
153
+ * ];
154
+ * const result = processMessagesForSystemSupport(messages, undefined, false);
155
+ * // result.nonSystemMessages = [{ role: 'user', content: 'Hello' }]
156
+ * // result.systemContent.combinedSystemContent = 'Be helpful'
157
+ * ```
158
+ */
159
+ export declare function processMessagesForSystemSupport<T extends GenericMessage>(messages: T[], requestSystemMessage: string | undefined, supportsSystemMessage: boolean): {
160
+ nonSystemMessages: T[];
161
+ systemContent: SystemContentResult;
162
+ };