@tpitre/story-ui 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.sample +82 -11
- package/README.md +89 -0
- package/dist/cli/deploy.d.ts +17 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +696 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +26 -2
- package/dist/cli/setup.d.ts +11 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +437 -110
- package/dist/mcp-server/index.d.ts +2 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +120 -2
- package/dist/mcp-server/mcp-stdio-server.d.ts +3 -0
- package/dist/mcp-server/mcp-stdio-server.d.ts.map +1 -0
- package/dist/mcp-server/mcp-stdio-server.js +8 -1
- package/dist/mcp-server/routes/claude.d.ts +3 -0
- package/dist/mcp-server/routes/claude.d.ts.map +1 -0
- package/dist/mcp-server/routes/claude.js +60 -23
- package/dist/mcp-server/routes/components.d.ts +4 -0
- package/dist/mcp-server/routes/components.d.ts.map +1 -0
- package/dist/mcp-server/routes/frameworks.d.ts +38 -0
- package/dist/mcp-server/routes/frameworks.d.ts.map +1 -0
- package/dist/mcp-server/routes/frameworks.js +183 -0
- package/dist/mcp-server/routes/generateStory.d.ts +3 -0
- package/dist/mcp-server/routes/generateStory.d.ts.map +1 -0
- package/dist/mcp-server/routes/generateStory.js +160 -76
- package/dist/mcp-server/routes/generateStoryStream.d.ts +12 -0
- package/dist/mcp-server/routes/generateStoryStream.d.ts.map +1 -0
- package/dist/mcp-server/routes/generateStoryStream.js +947 -0
- package/dist/mcp-server/routes/hybridStories.d.ts +18 -0
- package/dist/mcp-server/routes/hybridStories.d.ts.map +1 -0
- package/dist/mcp-server/routes/mcpRemote.d.ts +14 -0
- package/dist/mcp-server/routes/mcpRemote.d.ts.map +1 -0
- package/dist/mcp-server/routes/mcpRemote.js +489 -0
- package/dist/mcp-server/routes/memoryStories.d.ts +26 -0
- package/dist/mcp-server/routes/memoryStories.d.ts.map +1 -0
- package/dist/mcp-server/routes/providers.d.ts +89 -0
- package/dist/mcp-server/routes/providers.d.ts.map +1 -0
- package/dist/mcp-server/routes/providers.js +369 -0
- package/dist/mcp-server/routes/storySync.d.ts +26 -0
- package/dist/mcp-server/routes/storySync.d.ts.map +1 -0
- package/dist/mcp-server/routes/streamTypes.d.ts +110 -0
- package/dist/mcp-server/routes/streamTypes.d.ts.map +1 -0
- package/dist/mcp-server/routes/streamTypes.js +18 -0
- package/dist/mcp-server/sessionManager.d.ts +50 -0
- package/dist/mcp-server/sessionManager.d.ts.map +1 -0
- package/dist/story-generator/componentBlacklist.d.ts +21 -0
- package/dist/story-generator/componentBlacklist.d.ts.map +1 -0
- package/dist/story-generator/componentDiscovery.d.ts +28 -0
- package/dist/story-generator/componentDiscovery.d.ts.map +1 -0
- package/dist/story-generator/componentRegistryGenerator.d.ts +49 -0
- package/dist/story-generator/componentRegistryGenerator.d.ts.map +1 -0
- package/dist/story-generator/componentRegistryGenerator.js +205 -0
- package/dist/story-generator/configLoader.d.ts +33 -0
- package/dist/story-generator/configLoader.d.ts.map +1 -0
- package/dist/story-generator/considerationsLoader.d.ts +32 -0
- package/dist/story-generator/considerationsLoader.d.ts.map +1 -0
- package/dist/story-generator/documentation-sources.d.ts +28 -0
- package/dist/story-generator/documentation-sources.d.ts.map +1 -0
- package/dist/story-generator/documentationLoader.d.ts +64 -0
- package/dist/story-generator/documentationLoader.d.ts.map +1 -0
- package/dist/story-generator/dynamicPackageDiscovery.d.ts +97 -0
- package/dist/story-generator/dynamicPackageDiscovery.d.ts.map +1 -0
- package/dist/story-generator/enhancedComponentDiscovery.d.ts +125 -0
- package/dist/story-generator/enhancedComponentDiscovery.d.ts.map +1 -0
- package/dist/story-generator/enhancedComponentDiscovery.js +111 -11
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/angular-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/angular-adapter.js +427 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts +75 -0
- package/dist/story-generator/framework-adapters/base-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/base-adapter.js +147 -0
- package/dist/story-generator/framework-adapters/framework-detector.d.ts +55 -0
- package/dist/story-generator/framework-adapters/framework-detector.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/framework-detector.js +323 -0
- package/dist/story-generator/framework-adapters/index.d.ts +97 -0
- package/dist/story-generator/framework-adapters/index.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/index.js +198 -0
- package/dist/story-generator/framework-adapters/react-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/react-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/react-adapter.js +316 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.d.ts +40 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/svelte-adapter.js +372 -0
- package/dist/story-generator/framework-adapters/types.d.ts +182 -0
- package/dist/story-generator/framework-adapters/types.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/types.js +8 -0
- package/dist/story-generator/framework-adapters/vue-adapter.d.ts +36 -0
- package/dist/story-generator/framework-adapters/vue-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/vue-adapter.js +336 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts +54 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.d.ts.map +1 -0
- package/dist/story-generator/framework-adapters/web-components-adapter.js +387 -0
- package/dist/story-generator/generateStory.d.ts +7 -0
- package/dist/story-generator/generateStory.d.ts.map +1 -0
- package/dist/story-generator/gitignoreManager.d.ts +50 -0
- package/dist/story-generator/gitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/imageProcessor.d.ts +80 -0
- package/dist/story-generator/imageProcessor.d.ts.map +1 -0
- package/dist/story-generator/imageProcessor.js +391 -0
- package/dist/story-generator/inMemoryStoryService.d.ts +89 -0
- package/dist/story-generator/inMemoryStoryService.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/base-provider.d.ts +36 -0
- package/dist/story-generator/llm-providers/base-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/base-provider.js +135 -0
- package/dist/story-generator/llm-providers/claude-provider.d.ts +23 -0
- package/dist/story-generator/llm-providers/claude-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/claude-provider.js +414 -0
- package/dist/story-generator/llm-providers/gemini-provider.d.ts +24 -0
- package/dist/story-generator/llm-providers/gemini-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/gemini-provider.js +406 -0
- package/dist/story-generator/llm-providers/index.d.ts +63 -0
- package/dist/story-generator/llm-providers/index.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/index.js +169 -0
- package/dist/story-generator/llm-providers/openai-provider.d.ts +24 -0
- package/dist/story-generator/llm-providers/openai-provider.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/openai-provider.js +458 -0
- package/dist/story-generator/llm-providers/settings-manager.d.ts +75 -0
- package/dist/story-generator/llm-providers/settings-manager.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/settings-manager.js +173 -0
- package/dist/story-generator/llm-providers/story-llm-service.d.ts +79 -0
- package/dist/story-generator/llm-providers/story-llm-service.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/story-llm-service.js +240 -0
- package/dist/story-generator/llm-providers/types.d.ts +153 -0
- package/dist/story-generator/llm-providers/types.d.ts.map +1 -0
- package/dist/story-generator/llm-providers/types.js +8 -0
- package/dist/story-generator/logger.d.ts +14 -0
- package/dist/story-generator/logger.d.ts.map +1 -0
- package/dist/story-generator/logger.js +96 -29
- package/dist/story-generator/postProcessStory.d.ts +6 -0
- package/dist/story-generator/postProcessStory.d.ts.map +1 -0
- package/dist/story-generator/productionGitignoreManager.d.ts +91 -0
- package/dist/story-generator/productionGitignoreManager.d.ts.map +1 -0
- package/dist/story-generator/promptGenerator.d.ts +48 -0
- package/dist/story-generator/promptGenerator.d.ts.map +1 -0
- package/dist/story-generator/promptGenerator.js +186 -1
- package/dist/story-generator/storyHistory.d.ts +44 -0
- package/dist/story-generator/storyHistory.d.ts.map +1 -0
- package/dist/story-generator/storySync.d.ts +68 -0
- package/dist/story-generator/storySync.d.ts.map +1 -0
- package/dist/story-generator/storyTracker.d.ts +48 -0
- package/dist/story-generator/storyTracker.d.ts.map +1 -0
- package/dist/story-generator/storyValidator.d.ts +6 -0
- package/dist/story-generator/storyValidator.d.ts.map +1 -0
- package/dist/story-generator/universalDesignSystemAdapter.d.ts +68 -0
- package/dist/story-generator/universalDesignSystemAdapter.d.ts.map +1 -0
- package/dist/story-generator/universalDesignSystemAdapter.js +138 -1
- package/dist/story-generator/urlRedirectService.d.ts +21 -0
- package/dist/story-generator/urlRedirectService.d.ts.map +1 -0
- package/dist/story-generator/validateStory.d.ts +19 -0
- package/dist/story-generator/validateStory.d.ts.map +1 -0
- package/dist/story-generator/validateStory.js +6 -2
- package/dist/story-generator/visionPrompts.d.ts +88 -0
- package/dist/story-generator/visionPrompts.d.ts.map +1 -0
- package/dist/story-generator/visionPrompts.js +462 -0
- package/dist/story-ui.config.d.ts +78 -0
- package/dist/story-ui.config.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts +4 -0
- package/dist/templates/StoryUI/StoryUIPanel.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.js +1874 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts +18 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.d.ts.map +1 -0
- package/dist/templates/StoryUI/StoryUIPanel.stories.js +37 -0
- package/dist/templates/StoryUI/index.d.ts +3 -0
- package/dist/templates/StoryUI/index.d.ts.map +1 -0
- package/dist/templates/StoryUI/index.js +2 -0
- package/package.json +17 -3
- package/templates/StoryUI/StoryUIPanel.tsx +1960 -384
- package/templates/StoryUI/index.tsx +1 -1
- package/templates/StoryUI/manager.tsx +264 -0
- package/templates/production-app/.env.example +11 -0
- package/templates/production-app/index.html +66 -0
- package/templates/production-app/package.json +30 -0
- package/templates/production-app/public/favicon.svg +5 -0
- package/templates/production-app/src/App.tsx +1157 -0
- package/templates/production-app/src/LivePreviewRenderer.tsx +420 -0
- package/templates/production-app/src/componentRegistry.ts +315 -0
- package/templates/production-app/src/considerations.ts +16 -0
- package/templates/production-app/src/index.css +284 -0
- package/templates/production-app/src/main.tsx +25 -0
- package/templates/production-app/tsconfig.json +32 -0
- package/templates/production-app/tsconfig.node.json +11 -0
- package/templates/production-app/vite.config.ts +83 -0
- package/templates/react-import-rule.json +2 -2
- package/dist/index.js +0 -12
- package/dist/story-ui.config.loader.js +0 -205
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI LLM Provider
|
|
3
|
+
*
|
|
4
|
+
* Implementation of the LLM provider interface for OpenAI's GPT models.
|
|
5
|
+
*/
|
|
6
|
+
import { BaseLLMProvider } from './base-provider.js';
|
|
7
|
+
import { logger } from '../logger.js';
|
|
8
|
+
// OpenAI model definitions - Updated November 2025
|
|
9
|
+
// Reference: https://openai.com/index/introducing-gpt-5/
|
|
10
|
+
const OPENAI_MODELS = [
|
|
11
|
+
// GPT-5.1 Series - Latest (November 2025)
|
|
12
|
+
{
|
|
13
|
+
id: 'gpt-5.1',
|
|
14
|
+
name: 'GPT-5.1',
|
|
15
|
+
provider: 'openai',
|
|
16
|
+
contextWindow: 256000,
|
|
17
|
+
maxOutputTokens: 32768,
|
|
18
|
+
supportsVision: true,
|
|
19
|
+
supportsDocuments: true,
|
|
20
|
+
supportsFunctionCalling: true,
|
|
21
|
+
supportsStreaming: true,
|
|
22
|
+
supportsReasoning: true, // Adaptive reasoning with reasoning_effort parameter
|
|
23
|
+
inputPricePer1kTokens: 0.005,
|
|
24
|
+
outputPricePer1kTokens: 0.015,
|
|
25
|
+
description: 'Latest GPT-5 series. 76.3% on SWE-bench. Adaptive reasoning capability.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'gpt-5.1-chat-latest',
|
|
29
|
+
name: 'GPT-5.1 Instant',
|
|
30
|
+
provider: 'openai',
|
|
31
|
+
contextWindow: 256000,
|
|
32
|
+
maxOutputTokens: 32768,
|
|
33
|
+
supportsVision: true,
|
|
34
|
+
supportsDocuments: true,
|
|
35
|
+
supportsFunctionCalling: true,
|
|
36
|
+
supportsStreaming: true,
|
|
37
|
+
supportsReasoning: true,
|
|
38
|
+
inputPricePer1kTokens: 0.003,
|
|
39
|
+
outputPricePer1kTokens: 0.012,
|
|
40
|
+
description: 'More conversational GPT-5.1 with improved instruction following.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'gpt-5.1-thinking',
|
|
44
|
+
name: 'GPT-5.1 Thinking',
|
|
45
|
+
provider: 'openai',
|
|
46
|
+
contextWindow: 256000,
|
|
47
|
+
maxOutputTokens: 65536,
|
|
48
|
+
supportsVision: true,
|
|
49
|
+
supportsDocuments: true,
|
|
50
|
+
supportsFunctionCalling: true,
|
|
51
|
+
supportsStreaming: true,
|
|
52
|
+
supportsReasoning: true,
|
|
53
|
+
inputPricePer1kTokens: 0.008,
|
|
54
|
+
outputPricePer1kTokens: 0.024,
|
|
55
|
+
description: 'Extended thinking mode for complex reasoning tasks.',
|
|
56
|
+
},
|
|
57
|
+
// GPT-5 Original (August 2025)
|
|
58
|
+
{
|
|
59
|
+
id: 'gpt-5',
|
|
60
|
+
name: 'GPT-5',
|
|
61
|
+
provider: 'openai',
|
|
62
|
+
contextWindow: 200000,
|
|
63
|
+
maxOutputTokens: 32768,
|
|
64
|
+
supportsVision: true,
|
|
65
|
+
supportsDocuments: true,
|
|
66
|
+
supportsFunctionCalling: true,
|
|
67
|
+
supportsStreaming: true,
|
|
68
|
+
supportsReasoning: true,
|
|
69
|
+
inputPricePer1kTokens: 0.005,
|
|
70
|
+
outputPricePer1kTokens: 0.015,
|
|
71
|
+
description: 'Multimodal foundation model combining reasoning and general capabilities.',
|
|
72
|
+
},
|
|
73
|
+
// GPT-4o Series
|
|
74
|
+
{
|
|
75
|
+
id: 'gpt-4o',
|
|
76
|
+
name: 'GPT-4o',
|
|
77
|
+
provider: 'openai',
|
|
78
|
+
contextWindow: 128000,
|
|
79
|
+
maxOutputTokens: 16384,
|
|
80
|
+
supportsVision: true,
|
|
81
|
+
supportsDocuments: false,
|
|
82
|
+
supportsFunctionCalling: true,
|
|
83
|
+
supportsStreaming: true,
|
|
84
|
+
inputPricePer1kTokens: 0.0025,
|
|
85
|
+
outputPricePer1kTokens: 0.01,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'gpt-4o-mini',
|
|
89
|
+
name: 'GPT-4o Mini',
|
|
90
|
+
provider: 'openai',
|
|
91
|
+
contextWindow: 128000,
|
|
92
|
+
maxOutputTokens: 16384,
|
|
93
|
+
supportsVision: true,
|
|
94
|
+
supportsDocuments: false,
|
|
95
|
+
supportsFunctionCalling: true,
|
|
96
|
+
supportsStreaming: true,
|
|
97
|
+
inputPricePer1kTokens: 0.00015,
|
|
98
|
+
outputPricePer1kTokens: 0.0006,
|
|
99
|
+
},
|
|
100
|
+
// o1 Reasoning Series
|
|
101
|
+
{
|
|
102
|
+
id: 'o1',
|
|
103
|
+
name: 'o1 (Reasoning)',
|
|
104
|
+
provider: 'openai',
|
|
105
|
+
contextWindow: 200000,
|
|
106
|
+
maxOutputTokens: 100000,
|
|
107
|
+
supportsVision: true,
|
|
108
|
+
supportsDocuments: false,
|
|
109
|
+
supportsFunctionCalling: true,
|
|
110
|
+
supportsStreaming: true,
|
|
111
|
+
inputPricePer1kTokens: 0.015,
|
|
112
|
+
outputPricePer1kTokens: 0.06,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'o1-mini',
|
|
116
|
+
name: 'o1 Mini (Reasoning)',
|
|
117
|
+
provider: 'openai',
|
|
118
|
+
contextWindow: 128000,
|
|
119
|
+
maxOutputTokens: 65536,
|
|
120
|
+
supportsVision: false,
|
|
121
|
+
supportsDocuments: false,
|
|
122
|
+
supportsFunctionCalling: true,
|
|
123
|
+
supportsStreaming: true,
|
|
124
|
+
inputPricePer1kTokens: 0.003,
|
|
125
|
+
outputPricePer1kTokens: 0.012,
|
|
126
|
+
},
|
|
127
|
+
// Legacy GPT-4 Series
|
|
128
|
+
{
|
|
129
|
+
id: 'gpt-4-turbo',
|
|
130
|
+
name: 'GPT-4 Turbo',
|
|
131
|
+
provider: 'openai',
|
|
132
|
+
contextWindow: 128000,
|
|
133
|
+
maxOutputTokens: 4096,
|
|
134
|
+
supportsVision: true,
|
|
135
|
+
supportsDocuments: false,
|
|
136
|
+
supportsFunctionCalling: true,
|
|
137
|
+
supportsStreaming: true,
|
|
138
|
+
inputPricePer1kTokens: 0.01,
|
|
139
|
+
outputPricePer1kTokens: 0.03,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'gpt-3.5-turbo',
|
|
143
|
+
name: 'GPT-3.5 Turbo (Legacy)',
|
|
144
|
+
provider: 'openai',
|
|
145
|
+
contextWindow: 16385,
|
|
146
|
+
maxOutputTokens: 4096,
|
|
147
|
+
supportsVision: false,
|
|
148
|
+
supportsDocuments: false,
|
|
149
|
+
supportsFunctionCalling: true,
|
|
150
|
+
supportsStreaming: true,
|
|
151
|
+
inputPricePer1kTokens: 0.0005,
|
|
152
|
+
outputPricePer1kTokens: 0.0015,
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
// Default model
|
|
156
|
+
const DEFAULT_MODEL = 'gpt-4o';
|
|
157
|
+
// API configuration
|
|
158
|
+
const OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions';
|
|
159
|
+
export class OpenAIProvider extends BaseLLMProvider {
|
|
160
|
+
constructor(config) {
|
|
161
|
+
super(config);
|
|
162
|
+
this.name = 'OpenAI';
|
|
163
|
+
this.type = 'openai';
|
|
164
|
+
this.supportedModels = OPENAI_MODELS;
|
|
165
|
+
// Set the provider type after base constructor
|
|
166
|
+
this.setProviderType();
|
|
167
|
+
// Set default model if not provided
|
|
168
|
+
if (!this.config.model) {
|
|
169
|
+
this.config.model = DEFAULT_MODEL;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async chat(messages, options) {
|
|
173
|
+
this.validateMessages(messages);
|
|
174
|
+
this.logRequest(messages, options);
|
|
175
|
+
const apiKey = this.config.apiKey;
|
|
176
|
+
if (!apiKey) {
|
|
177
|
+
throw new Error('OpenAI API key not configured');
|
|
178
|
+
}
|
|
179
|
+
const model = options?.model || this.config.model;
|
|
180
|
+
const openaiMessages = this.convertMessages(messages, options?.systemPrompt);
|
|
181
|
+
const requestBody = {
|
|
182
|
+
model,
|
|
183
|
+
messages: openaiMessages,
|
|
184
|
+
max_tokens: options?.maxTokens || this.getSelectedModel()?.maxOutputTokens || 4096,
|
|
185
|
+
};
|
|
186
|
+
// Add optional parameters
|
|
187
|
+
if (options?.temperature !== undefined) {
|
|
188
|
+
requestBody.temperature = options.temperature;
|
|
189
|
+
}
|
|
190
|
+
if (options?.topP !== undefined) {
|
|
191
|
+
requestBody.top_p = options.topP;
|
|
192
|
+
}
|
|
193
|
+
if (options?.stopSequences?.length) {
|
|
194
|
+
requestBody.stop = options.stopSequences;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const response = await fetch(this.config.baseUrl || OPENAI_API_URL, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: {
|
|
200
|
+
'Content-Type': 'application/json',
|
|
201
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
202
|
+
...(this.config.organizationId && { 'OpenAI-Organization': this.config.organizationId }),
|
|
203
|
+
},
|
|
204
|
+
body: JSON.stringify(requestBody),
|
|
205
|
+
signal: AbortSignal.timeout(this.config.timeout || 120000),
|
|
206
|
+
});
|
|
207
|
+
if (!response.ok) {
|
|
208
|
+
const errorBody = await response.text();
|
|
209
|
+
logger.error('OpenAI API error response', { status: response.status, body: errorBody });
|
|
210
|
+
throw new Error(`OpenAI API error: ${response.status} - ${errorBody}`);
|
|
211
|
+
}
|
|
212
|
+
const data = (await response.json());
|
|
213
|
+
const chatResponse = this.convertResponse(data);
|
|
214
|
+
this.logResponse(chatResponse);
|
|
215
|
+
return chatResponse;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (error instanceof Error && error.name === 'TimeoutError') {
|
|
219
|
+
throw new Error(`OpenAI API request timed out after ${this.config.timeout}ms`);
|
|
220
|
+
}
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async *chatStream(messages, options) {
|
|
225
|
+
this.validateMessages(messages);
|
|
226
|
+
this.logRequest(messages, options);
|
|
227
|
+
const apiKey = this.config.apiKey;
|
|
228
|
+
if (!apiKey) {
|
|
229
|
+
yield { type: 'error', error: 'OpenAI API key not configured' };
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const model = options?.model || this.config.model;
|
|
233
|
+
const openaiMessages = this.convertMessages(messages, options?.systemPrompt);
|
|
234
|
+
const requestBody = {
|
|
235
|
+
model,
|
|
236
|
+
messages: openaiMessages,
|
|
237
|
+
max_tokens: options?.maxTokens || this.getSelectedModel()?.maxOutputTokens || 4096,
|
|
238
|
+
stream: true,
|
|
239
|
+
};
|
|
240
|
+
if (options?.temperature !== undefined) {
|
|
241
|
+
requestBody.temperature = options.temperature;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const response = await fetch(this.config.baseUrl || OPENAI_API_URL, {
|
|
245
|
+
method: 'POST',
|
|
246
|
+
headers: {
|
|
247
|
+
'Content-Type': 'application/json',
|
|
248
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
249
|
+
...(this.config.organizationId && { 'OpenAI-Organization': this.config.organizationId }),
|
|
250
|
+
},
|
|
251
|
+
body: JSON.stringify(requestBody),
|
|
252
|
+
signal: AbortSignal.timeout(this.config.timeout || 120000),
|
|
253
|
+
});
|
|
254
|
+
if (!response.ok) {
|
|
255
|
+
const errorBody = await response.text();
|
|
256
|
+
yield { type: 'error', error: `OpenAI API error: ${response.status} - ${errorBody}` };
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const reader = response.body?.getReader();
|
|
260
|
+
if (!reader) {
|
|
261
|
+
yield { type: 'error', error: 'No response body' };
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const decoder = new TextDecoder();
|
|
265
|
+
let buffer = '';
|
|
266
|
+
let promptTokens = 0;
|
|
267
|
+
let completionTokens = 0;
|
|
268
|
+
while (true) {
|
|
269
|
+
const { done, value } = await reader.read();
|
|
270
|
+
if (done)
|
|
271
|
+
break;
|
|
272
|
+
buffer += decoder.decode(value, { stream: true });
|
|
273
|
+
const lines = buffer.split('\n');
|
|
274
|
+
buffer = lines.pop() || '';
|
|
275
|
+
for (const line of lines) {
|
|
276
|
+
if (line.startsWith('data: ')) {
|
|
277
|
+
const data = line.slice(6);
|
|
278
|
+
if (data === '[DONE]')
|
|
279
|
+
continue;
|
|
280
|
+
try {
|
|
281
|
+
const event = JSON.parse(data);
|
|
282
|
+
if (event.choices?.[0]?.delta?.content) {
|
|
283
|
+
yield { type: 'text', content: event.choices[0].delta.content };
|
|
284
|
+
}
|
|
285
|
+
// Usage may be included in the final message
|
|
286
|
+
if (event.usage) {
|
|
287
|
+
promptTokens = event.usage.prompt_tokens || 0;
|
|
288
|
+
completionTokens = event.usage.completion_tokens || 0;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
// Skip malformed JSON
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
yield {
|
|
298
|
+
type: 'done',
|
|
299
|
+
usage: {
|
|
300
|
+
promptTokens,
|
|
301
|
+
completionTokens,
|
|
302
|
+
totalTokens: promptTokens + completionTokens,
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
yield {
|
|
308
|
+
type: 'error',
|
|
309
|
+
error: error instanceof Error ? error.message : String(error),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async validateApiKey(apiKey) {
|
|
314
|
+
try {
|
|
315
|
+
// Make a minimal API call to validate the key
|
|
316
|
+
const response = await fetch(OPENAI_API_URL, {
|
|
317
|
+
method: 'POST',
|
|
318
|
+
headers: {
|
|
319
|
+
'Content-Type': 'application/json',
|
|
320
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
321
|
+
},
|
|
322
|
+
body: JSON.stringify({
|
|
323
|
+
model: 'gpt-3.5-turbo',
|
|
324
|
+
max_tokens: 1,
|
|
325
|
+
messages: [{ role: 'user', content: 'Hi' }],
|
|
326
|
+
}),
|
|
327
|
+
signal: AbortSignal.timeout(10000),
|
|
328
|
+
});
|
|
329
|
+
if (response.ok) {
|
|
330
|
+
return {
|
|
331
|
+
valid: true,
|
|
332
|
+
models: this.supportedModels,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
const errorBody = await response.text();
|
|
336
|
+
// Check for specific error types
|
|
337
|
+
if (response.status === 401) {
|
|
338
|
+
return {
|
|
339
|
+
valid: false,
|
|
340
|
+
error: 'Invalid API key',
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
valid: false,
|
|
345
|
+
error: `API validation failed: ${errorBody}`,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
return {
|
|
350
|
+
valid: false,
|
|
351
|
+
error: error instanceof Error ? error.message : 'Validation failed',
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Convert our message format to OpenAI format
|
|
356
|
+
convertMessages(messages, systemPrompt) {
|
|
357
|
+
const result = [];
|
|
358
|
+
// Add system prompt if provided
|
|
359
|
+
if (systemPrompt) {
|
|
360
|
+
result.push({ role: 'system', content: systemPrompt });
|
|
361
|
+
}
|
|
362
|
+
// Convert remaining messages
|
|
363
|
+
for (const msg of messages) {
|
|
364
|
+
if (msg.role === 'system') {
|
|
365
|
+
// Add system messages directly
|
|
366
|
+
result.push({
|
|
367
|
+
role: 'system',
|
|
368
|
+
content: typeof msg.content === 'string' ? msg.content : this.extractTextContent(msg.content),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
result.push({
|
|
373
|
+
role: msg.role,
|
|
374
|
+
content: this.convertContent(msg.content),
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
extractTextContent(content) {
|
|
381
|
+
return content
|
|
382
|
+
.filter(item => item.type === 'text')
|
|
383
|
+
.map(item => item.text)
|
|
384
|
+
.join('\n');
|
|
385
|
+
}
|
|
386
|
+
convertContent(content) {
|
|
387
|
+
if (typeof content === 'string') {
|
|
388
|
+
return content;
|
|
389
|
+
}
|
|
390
|
+
return content.map(item => {
|
|
391
|
+
if (item.type === 'text') {
|
|
392
|
+
return { type: 'text', text: item.text };
|
|
393
|
+
}
|
|
394
|
+
if (item.type === 'image') {
|
|
395
|
+
const imageContent = item;
|
|
396
|
+
// OpenAI expects either a URL or a base64 data URL
|
|
397
|
+
const imageUrl = imageContent.source.url ||
|
|
398
|
+
`data:${imageContent.source.mediaType || 'image/png'};base64,${imageContent.source.data}`;
|
|
399
|
+
return {
|
|
400
|
+
type: 'image_url',
|
|
401
|
+
image_url: {
|
|
402
|
+
url: imageUrl,
|
|
403
|
+
detail: 'auto',
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
// Document type - convert to text representation
|
|
408
|
+
if (item.type === 'document') {
|
|
409
|
+
return {
|
|
410
|
+
type: 'text',
|
|
411
|
+
text: `[Document: ${item.source.name || 'unnamed'}]`,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
return { type: 'text', text: '' };
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
convertResponse(data) {
|
|
418
|
+
const choice = data.choices[0];
|
|
419
|
+
const content = choice?.message?.content || '';
|
|
420
|
+
return {
|
|
421
|
+
id: data.id,
|
|
422
|
+
model: data.model,
|
|
423
|
+
content,
|
|
424
|
+
finishReason: this.mapFinishReason(choice?.finish_reason),
|
|
425
|
+
usage: {
|
|
426
|
+
promptTokens: data.usage.prompt_tokens,
|
|
427
|
+
completionTokens: data.usage.completion_tokens,
|
|
428
|
+
totalTokens: data.usage.total_tokens,
|
|
429
|
+
},
|
|
430
|
+
raw: data,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
mapFinishReason(finishReason) {
|
|
434
|
+
switch (finishReason) {
|
|
435
|
+
case 'stop':
|
|
436
|
+
return 'stop';
|
|
437
|
+
case 'length':
|
|
438
|
+
return 'length';
|
|
439
|
+
case 'tool_calls':
|
|
440
|
+
case 'function_call':
|
|
441
|
+
return 'tool_calls';
|
|
442
|
+
case 'content_filter':
|
|
443
|
+
return 'content_filter';
|
|
444
|
+
default:
|
|
445
|
+
return 'stop';
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
// Token estimation using tiktoken approximation
|
|
449
|
+
estimateTokens(text) {
|
|
450
|
+
// Rough estimate: ~4 characters per token for English text
|
|
451
|
+
// OpenAI uses cl100k_base encoding for newer models
|
|
452
|
+
return Math.ceil(text.length / 4);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// Factory function
|
|
456
|
+
export function createOpenAIProvider(config) {
|
|
457
|
+
return new OpenAIProvider(config);
|
|
458
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Settings Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages hybrid configuration for LLM provider/model selection.
|
|
5
|
+
* Combines environment variables (DevOps guardrails) with user preferences.
|
|
6
|
+
*
|
|
7
|
+
* Environment Variables:
|
|
8
|
+
* - DEFAULT_PROVIDER: Default provider (claude, openai, gemini)
|
|
9
|
+
* - DEFAULT_MODEL: Default model ID
|
|
10
|
+
* - ALLOWED_MODELS: Comma-separated list of allowed model IDs (optional)
|
|
11
|
+
* - ALLOWED_PROVIDERS: Comma-separated list of allowed providers (optional)
|
|
12
|
+
* - SINGLE_PROVIDER_MODE: Hide provider selection (true/false)
|
|
13
|
+
*/
|
|
14
|
+
import { ProviderType } from './types.js';
|
|
15
|
+
export interface UserSettings {
|
|
16
|
+
selectedProvider?: ProviderType;
|
|
17
|
+
selectedModel?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SettingsConfig {
|
|
20
|
+
defaultProvider: ProviderType;
|
|
21
|
+
defaultModel: string;
|
|
22
|
+
allowedProviders: ProviderType[];
|
|
23
|
+
allowedModels: string[];
|
|
24
|
+
singleProviderMode: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface AvailableOption {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
isDefault?: boolean;
|
|
31
|
+
isRecommended?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface SettingsResponse {
|
|
34
|
+
providers: AvailableOption[];
|
|
35
|
+
models: AvailableOption[];
|
|
36
|
+
currentProvider: ProviderType;
|
|
37
|
+
currentModel: string;
|
|
38
|
+
config: {
|
|
39
|
+
singleProviderMode: boolean;
|
|
40
|
+
hasRestrictions: boolean;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load settings configuration from environment
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadSettingsConfig(): SettingsConfig;
|
|
47
|
+
/**
|
|
48
|
+
* Get available providers for UI selection
|
|
49
|
+
*/
|
|
50
|
+
export declare function getAvailableProviders(config: SettingsConfig): AvailableOption[];
|
|
51
|
+
/**
|
|
52
|
+
* Get available models for a provider
|
|
53
|
+
*/
|
|
54
|
+
export declare function getAvailableModels(provider: ProviderType, config: SettingsConfig): AvailableOption[];
|
|
55
|
+
/**
|
|
56
|
+
* Validate user's model selection against configuration
|
|
57
|
+
*/
|
|
58
|
+
export declare function validateSelection(provider: ProviderType, model: string, config: SettingsConfig): {
|
|
59
|
+
valid: boolean;
|
|
60
|
+
error?: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Get complete settings response for UI
|
|
64
|
+
*/
|
|
65
|
+
export declare function getSettingsForUI(currentProvider?: ProviderType, currentModel?: string): SettingsResponse;
|
|
66
|
+
/**
|
|
67
|
+
* Apply user settings and return effective provider/model
|
|
68
|
+
*/
|
|
69
|
+
export declare function applyUserSettings(settings: UserSettings): {
|
|
70
|
+
provider: ProviderType;
|
|
71
|
+
model: string;
|
|
72
|
+
applied: boolean;
|
|
73
|
+
fallbackReason?: string;
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=settings-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-manager.d.ts","sourceRoot":"","sources":["../../../story-generator/llm-providers/settings-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAA0B,MAAM,YAAY,CAAC;AAIlE,MAAM,WAAW,YAAY;IAC3B,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;CAExB;AAED,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,YAAY,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,YAAY,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,eAAe,EAAE,YAAY,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE;QACN,kBAAkB,EAAE,OAAO,CAAC;QAC5B,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,cAAc,CA0BnD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,EAAE,CAyB/E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,cAAc,GACrB,eAAe,EAAE,CAmCnB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,cAAc,GACrB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA4BpC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,CAAC,EAAE,YAAY,EAC9B,YAAY,CAAC,EAAE,MAAM,GACpB,gBAAgB,CAuBlB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG;IACzD,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAuBA"}
|