feat(api): 支持通过路径段指定MODEL_PROVIDER并优化系统提示词提取逻辑
本次提交主要包含以下变更: 1. 在API服务器中新增通过URL路径段自动识别和切换MODEL_PROVIDER的功能,提升接口灵活性 2. 重构各提供商策略中的系统提示词提取逻辑,统一使用common.js中新增的extractSystemPromptFromRequestBody工具函数,提高代码复用性和维护性 3. 更新README文档,完善架构设计模式说明、数据流处理流程以及多模态内容和不同提供商的使用示例
This commit is contained in:
parent
30cf7784f1
commit
fcf6389856
8 changed files with 230 additions and 62 deletions
69
README-EN.md
69
README-EN.md
|
|
@ -54,19 +54,36 @@ Leaving behind the simple structure of the past, we have introduced a more profe
|
||||||
* Stores shared constants, utility functions, and common handlers for the project, making the code cleaner and more efficient.
|
* Stores shared constants, utility functions, and common handlers for the project, making the code cleaner and more efficient.
|
||||||
|
|
||||||
* **`src/gemini/`, `src/openai/`, `src/claude/`**: 📦 **Provider Implementation Directories**
|
* **`src/gemini/`, `src/openai/`, `src/claude/`**: 📦 **Provider Implementation Directories**
|
||||||
* Each directory contains the core logic, API calls, and strategy implementations for the corresponding service provider, with a clear structure that makes it easy for you to add more new service providers in the future. Among them, `src/openai/openai-kiro.js` provides a special implementation for the Kiro API.
|
* Each directory contains the core logic, API calls, and strategy implementations for the corresponding service provider, with a clear structure that makes it easy for you to add more new service providers in the future. Among them, `src/claude/claude-kiro.js` provides a special implementation for the Kiro API.
|
||||||
|
|
||||||
* **`tests/`**: 🧪 **Test Directory**
|
* **`tests/`**: 🧪 **Test Directory**
|
||||||
* Contains a complete integration test suite covering all API endpoints, authentication methods, and error handling scenarios to ensure project stability and reliability.
|
* Contains a complete integration test suite covering all API endpoints, authentication methods, and error handling scenarios to ensure project stability and reliability.
|
||||||
|
|
||||||
---
|
### 🏗️ Architecture Design Patterns
|
||||||
|
|
||||||
|
The project adopts multiple modern design patterns to ensure code maintainability and extensibility:
|
||||||
|
|
||||||
|
* **Adapter Pattern**: `src/adapter.js` provides a unified interface for different AI services
|
||||||
|
* **Strategy Pattern**: `src/provider-strategies.js` handles request/response conversion for different protocols
|
||||||
|
* **Factory Pattern**: Dynamically creates and manages service adapter instances
|
||||||
|
* **Singleton Pattern**: Caching and reusing service adapter instances
|
||||||
|
|
||||||
|
### 🔄 Data Flow Processing
|
||||||
|
|
||||||
|
1. **Request Reception**: HTTP server receives client requests
|
||||||
|
2. **Authentication Verification**: Unified verification of multiple authentication methods
|
||||||
|
3. **Protocol Recognition**: Identifies client protocol based on endpoint and request headers
|
||||||
|
4. **Format Conversion**: Converts requests to target provider format
|
||||||
|
5. **Service Call**: Calls specific AI service through adapter
|
||||||
|
6. **Response Conversion**: Converts service response back to client expected format
|
||||||
|
7. **Streaming Processing**: Supports real-time streaming response transmission
|
||||||
|
|
||||||
### 🔧 Usage Instructions
|
### 🔧 Usage Instructions
|
||||||
|
|
||||||
* **MCP Support**: While the built-in command functions of the original Gemini CLI are not available, this project perfectly supports MCP (Model Context Protocol) and can work with MCP-compatible clients for more powerful functionality extensions.
|
* **MCP Support**: While the built-in command functions of the original Gemini CLI are not available, this project perfectly supports MCP (Model Context Protocol) and can work with MCP-compatible clients for more powerful functionality extensions.
|
||||||
* **Multimodal Capabilities**: Supports multimodal inputs such as images and documents, providing you with a richer interactive experience.
|
* **Multimodal Capabilities**: Supports multimodal inputs such as images and documents, providing you with a richer interactive experience.
|
||||||
* **Latest Model Support**: Supports the latest **Kimi K2** and **GLM-4.5** models. Simply configure the corresponding OpenAI or Claude compatible interfaces in `config.json` to use them.
|
* **Latest Model Support**: Supports the latest **Kimi K2** and **GLM-4.5** models. Simply configure the corresponding OpenAI or Claude compatible interfaces in `config.json` to use them.
|
||||||
* **Kiro API**: Using Kiro API requires [Download Kiro client](https://aibook.ren/archives/kiro-install) and completing authorized login to generate kiro-auth-token.json. **Recommended for use with Claude Code for the best experience**. .
|
* **Kiro API**: Using Kiro API requires [Download Kiro client](https://aibook.ren/archives/kiro-install) and completing authorized login to generate kiro-auth-token.json. **Recommended for use with Claude Code for the best experience**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -81,9 +98,19 @@ Leaving behind the simple structure of the past, we have introduced a more profe
|
||||||
|
|
||||||
#### OpenAI Compatible Interface (`/v1/...`)
|
#### OpenAI Compatible Interface (`/v1/...`)
|
||||||
* 🌍 **Perfect Compatibility**: Implements the core `/v1/models` and `/v1/chat/completions` endpoints.
|
* 🌍 **Perfect Compatibility**: Implements the core `/v1/models` and `/v1/chat/completions` endpoints.
|
||||||
* 🔄 **Automatic Format Conversion**: Internally and seamlessly converts requests/responses between different model formats and the OpenAI format.
|
* 🔄 **Automatic Format Conversion**: Internally and seamlessly converts requests/responses between different model formats and the OpenAI format, supporting multimodal content.
|
||||||
* 💨 **Streaming Support**: Fully supports OpenAI's streaming responses (`"stream": true`), providing a typewriter-like real-time experience.
|
* 💨 **Streaming Support**: Fully supports OpenAI's streaming responses (`"stream": true`), providing a typewriter-like real-time experience.
|
||||||
|
|
||||||
|
#### Gemini Native Interface (`/v1beta/...`)
|
||||||
|
* 🎯 **Native Support**: Complete support for Gemini API's native format and features.
|
||||||
|
* 🔧 **Advanced Features**: Supports system instructions, tool calls, multimodal input and other advanced features.
|
||||||
|
* 📊 **Detailed Statistics**: Provides complete token usage statistics and model information.
|
||||||
|
|
||||||
|
#### Claude Native Interface (`/v1/messages`)
|
||||||
|
* 🤖 **Claude Dedicated**: Complete support for Claude Messages API format.
|
||||||
|
* 🛠️ **Tool Integration**: Supports Claude's tool usage and function calling features.
|
||||||
|
* 🎨 **Multimodal**: Supports images, audio and other input formats.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 Installation Guide
|
## 📦 Installation Guide
|
||||||
|
|
@ -206,6 +233,40 @@ All requests use the standard OpenAI format.
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* **Multimodal Content Generation**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:3000/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer 123456" \
|
||||||
|
-d '{
|
||||||
|
"model": "gemini-2.5-flash",
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": [
|
||||||
|
{"type": "text", "text": "Describe this image"},
|
||||||
|
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Using Different Providers (via Path)**
|
||||||
|
```bash
|
||||||
|
# Using Gemini
|
||||||
|
curl http://localhost:3000/gemini-cli-oauth/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer 123456" \
|
||||||
|
-d '{"model": "gemini-2.5-flash", "messages": [{"role": "user", "content": "Hello"}]}'
|
||||||
|
|
||||||
|
# Using Claude
|
||||||
|
curl http://localhost:3000/claude-custom/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer 123456" \
|
||||||
|
-d '{"model": "claude-3-opus-20240229", "messages": [{"role": "user", "content": "Hello"}]}'
|
||||||
|
```
|
||||||
* **Stream Generate Content**
|
* **Stream Generate Content**
|
||||||
```bash
|
```bash
|
||||||
curl http://localhost:3000/v1/chat/completions \
|
curl http://localhost:3000/v1/chat/completions \
|
||||||
|
|
|
||||||
69
README.md
69
README.md
|
|
@ -54,10 +54,29 @@
|
||||||
* 存放着项目共享的常量、工具函数和通用处理器,让代码更加整洁和高效。
|
* 存放着项目共享的常量、工具函数和通用处理器,让代码更加整洁和高效。
|
||||||
|
|
||||||
* **`src/gemini/`, `src/openai/`, `src/claude/`**: 📦 **提供商实现目录**
|
* **`src/gemini/`, `src/openai/`, `src/claude/`**: 📦 **提供商实现目录**
|
||||||
* 每个目录都包含了对应服务商的核心逻辑、API 调用和策略实现,结构清晰,便于您未来添加更多新的服务商。其中 `src/openai/openai-kiro.js` 提供了 Kiro API 的特殊实现。
|
* 每个目录都包含了对应服务商的核心逻辑、API 调用和策略实现,结构清晰,便于您未来添加更多新的服务商。其中 `src/claude/claude-kiro.js` 提供了 Kiro API 的特殊实现。
|
||||||
|
|
||||||
* **`tests/`**: 🧪 **测试目录**
|
* **`tests/`**: 🧪 **测试目录**
|
||||||
* 包含完整的集成测试套件,覆盖所有API端点、认证方式和错误处理场景,确保项目的稳定性和可靠性。
|
* 包含完整的集成测试套件,覆盖所有API端点、认证方式和错误处理场景,确保项目的稳定性和可靠性。支持针对不同提供商的独立测试和完整的HTTP集成测试。
|
||||||
|
|
||||||
|
### 🏗️ 架构设计模式
|
||||||
|
|
||||||
|
项目采用多种现代设计模式,确保代码的可维护性和扩展性:
|
||||||
|
|
||||||
|
* **适配器模式 (Adapter Pattern)**: `src/adapter.js` 为不同的 AI 服务提供统一接口
|
||||||
|
* **策略模式 (Strategy Pattern)**: `src/provider-strategies.js` 处理不同协议的请求/响应转换
|
||||||
|
* **工厂模式 (Factory Pattern)**: 动态创建和管理服务适配器实例
|
||||||
|
* **单例模式 (Singleton Pattern)**: 服务适配器实例的缓存和复用
|
||||||
|
|
||||||
|
### 🔄 数据流处理
|
||||||
|
|
||||||
|
1. **请求接收**: HTTP 服务器接收客户端请求
|
||||||
|
2. **认证验证**: 多种认证方式的统一验证
|
||||||
|
3. **协议识别**: 根据端点和请求头识别客户端协议
|
||||||
|
4. **格式转换**: 将请求转换为目标提供商格式
|
||||||
|
5. **服务调用**: 通过适配器调用具体的 AI 服务
|
||||||
|
6. **响应转换**: 将服务响应转换回客户端期望格式
|
||||||
|
7. **流式处理**: 支持实时流式响应传输
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -81,9 +100,19 @@
|
||||||
|
|
||||||
#### OpenAI 兼容接口 (`/v1/...`)
|
#### OpenAI 兼容接口 (`/v1/...`)
|
||||||
* 🌍 **完美兼容**: 实现了 `/v1/models` 和 `/v1/chat/completions` 核心端点。
|
* 🌍 **完美兼容**: 实现了 `/v1/models` 和 `/v1/chat/completions` 核心端点。
|
||||||
* 🔄 **自动格式转换**: 在内部自动将不同模型的请求/响应与 OpenAI 格式进行无缝转换。
|
* 🔄 **自动格式转换**: 在内部自动将不同模型的请求/响应与 OpenAI 格式进行无缝转换,支持多模态内容。
|
||||||
* 💨 **流式传输支持**: 完全支持 OpenAI 的流式响应 (`"stream": true`),提供打字机般的实时体验。
|
* 💨 **流式传输支持**: 完全支持 OpenAI 的流式响应 (`"stream": true`),提供打字机般的实时体验。
|
||||||
|
|
||||||
|
#### Gemini 原生接口 (`/v1beta/...`)
|
||||||
|
* 🎯 **原生支持**: 完整支持 Gemini API 的原生格式和功能。
|
||||||
|
* 🔧 **高级功能**: 支持系统指令、工具调用、多模态输入等高级特性。
|
||||||
|
* 📊 **详细统计**: 提供完整的 token 使用统计和模型信息。
|
||||||
|
|
||||||
|
#### Claude 原生接口 (`/v1/messages`)
|
||||||
|
* 🤖 **Claude 专用**: 完整支持 Claude Messages API 格式。
|
||||||
|
* 🛠️ **工具集成**: 支持 Claude 的工具使用和函数调用功能。
|
||||||
|
* 🎨 **多模态**: 支持图片、音频等多种输入格式。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 安装指南
|
## 📦 安装指南
|
||||||
|
|
@ -206,6 +235,40 @@
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* **多模态内容生成**
|
||||||
|
```bash
|
||||||
|
curl http://localhost:3000/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer 123456" \
|
||||||
|
-d '{
|
||||||
|
"model": "gemini-2.5-flash",
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": [
|
||||||
|
{"type": "text", "text": "描述这张图片"},
|
||||||
|
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
* **使用不同提供商 (通过路径)**
|
||||||
|
```bash
|
||||||
|
# 使用 Gemini
|
||||||
|
curl http://localhost:3000/gemini-cli-oauth/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer 123456" \
|
||||||
|
-d '{"model": "gemini-2.5-flash", "messages": [{"role": "user", "content": "Hello"}]}'
|
||||||
|
|
||||||
|
# 使用 Claude
|
||||||
|
curl http://localhost:3000/claude-custom/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer 123456" \
|
||||||
|
-d '{"model": "claude-3-opus-20240229", "messages": [{"role": "user", "content": "Hello"}]}'
|
||||||
|
```
|
||||||
* **流式生成内容**
|
* **流式生成内容**
|
||||||
```bash
|
```bash
|
||||||
curl http://localhost:3000/v1/chat/completions \
|
curl http://localhost:3000/v1/chat/completions \
|
||||||
|
|
|
||||||
|
|
@ -348,14 +348,9 @@ async function getSystemPromptFileContent(filePath) {
|
||||||
|
|
||||||
async function initApiService(config) { // Make getApiService exportable and accept config
|
async function initApiService(config) { // Make getApiService exportable and accept config
|
||||||
// Initialize all known service adapters at startup
|
// Initialize all known service adapters at startup
|
||||||
const providers = [
|
for (const provider of Object.values(MODEL_PROVIDER)) {
|
||||||
MODEL_PROVIDER.OPENAI_CUSTOM,
|
|
||||||
MODEL_PROVIDER.GEMINI_CLI,
|
|
||||||
MODEL_PROVIDER.CLAUDE_CUSTOM,
|
|
||||||
MODEL_PROVIDER.KIRO_API
|
|
||||||
];
|
|
||||||
for (const provider of providers) {
|
|
||||||
try {
|
try {
|
||||||
|
console.log(`[Initialization] Initializing service adapter for ${provider}...`);
|
||||||
getServiceAdapter({ ...config, MODEL_PROVIDER: provider });
|
getServiceAdapter({ ...config, MODEL_PROVIDER: provider });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`[Initialization Warning] Failed to initialize service adapter for ${provider}: ${error.message}`);
|
console.warn(`[Initialization Warning] Failed to initialize service adapter for ${provider}: ${error.message}`);
|
||||||
|
|
@ -392,11 +387,29 @@ function createRequestHandler(config) {
|
||||||
delete req.headers['model-provider'];
|
delete req.headers['model-provider'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiService = await getApiService(currentConfig);
|
|
||||||
const requestUrl = new URL(req.url, `http://${req.headers.host}`);
|
const requestUrl = new URL(req.url, `http://${req.headers.host}`);
|
||||||
const path = requestUrl.pathname;
|
let path = requestUrl.pathname;
|
||||||
const method = req.method;
|
// Check if the first path segment matches a MODEL_PROVIDER and switch if it does
|
||||||
|
const pathSegments = path.split('/').filter(segment => segment.length > 0);
|
||||||
|
if (pathSegments.length > 0) {
|
||||||
|
const firstSegment = pathSegments[0];
|
||||||
|
// Check if firstSegment is a valid MODEL_PROVIDER value
|
||||||
|
const isValidProvider = Object.values(MODEL_PROVIDER).includes(firstSegment);
|
||||||
|
if (firstSegment && isValidProvider) {
|
||||||
|
currentConfig.MODEL_PROVIDER = firstSegment;
|
||||||
|
console.log(`[Config] MODEL_PROVIDER overridden by path segment to: ${currentConfig.MODEL_PROVIDER}`);
|
||||||
|
// Remove the first segment from the path to maintain routing consistency
|
||||||
|
pathSegments.shift();
|
||||||
|
path = '/' + pathSegments.join('/');
|
||||||
|
// Update the requestUrl pathname as well
|
||||||
|
requestUrl.pathname = path;
|
||||||
|
} else if (firstSegment && !isValidProvider) {
|
||||||
|
console.log(`[Config] Ignoring invalid MODEL_PROVIDER in path segment: ${firstSegment}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiService = await getApiService(currentConfig);
|
||||||
|
const method = req.method;
|
||||||
if (method === 'OPTIONS') {
|
if (method === 'OPTIONS') {
|
||||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
console.log("OPTIONS REQUEST SUCCESS");
|
console.log("OPTIONS REQUEST SUCCESS");
|
||||||
|
|
|
||||||
|
|
@ -462,7 +462,7 @@ async initializeAuth(forceRefresh = false) {
|
||||||
.map(part => part.text)
|
.map(part => part.text)
|
||||||
.join('');
|
.join('');
|
||||||
}
|
}
|
||||||
return String(message.content || '');
|
return String(message.content || message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { ProviderStrategy } from '../provider-strategy.js';
|
import { ProviderStrategy } from '../provider-strategy.js';
|
||||||
|
import { extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../common.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Claude provider strategy implementation.
|
* Claude provider strategy implementation.
|
||||||
|
|
@ -51,10 +52,7 @@ class ClaudeStrategy extends ProviderStrategy {
|
||||||
return requestBody;
|
return requestBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
let existingSystemText = '';
|
const existingSystemText = extractSystemPromptFromRequestBody(requestBody, MODEL_PROTOCOL_PREFIX.CLAUDE);
|
||||||
if (requestBody.system) {
|
|
||||||
existingSystemText = requestBody.system;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append' && existingSystemText
|
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append' && existingSystemText
|
||||||
? `${existingSystemText}\n${filePromptContent}`
|
? `${existingSystemText}\n${filePromptContent}`
|
||||||
|
|
@ -67,14 +65,8 @@ class ClaudeStrategy extends ProviderStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
async manageSystemPrompt(requestBody) {
|
async manageSystemPrompt(requestBody) {
|
||||||
let incomingSystemText = '';
|
const incomingSystemText = extractSystemPromptFromRequestBody(requestBody, MODEL_PROTOCOL_PREFIX.CLAUDE);
|
||||||
if (typeof requestBody.system === 'string') {
|
await this._updateSystemPromptFile(incomingSystemText, MODEL_PROTOCOL_PREFIX.CLAUDE);
|
||||||
incomingSystemText = requestBody.system;
|
|
||||||
}
|
|
||||||
if (typeof requestBody.system === 'object') {
|
|
||||||
incomingSystemText = JSON.stringify(requestBody.system);
|
|
||||||
}
|
|
||||||
await this._updateSystemPromptFile(incomingSystemText, 'claude');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -475,3 +475,66 @@ export function handleError(res, error) {
|
||||||
};
|
};
|
||||||
res.end(JSON.stringify(errorPayload));
|
res.end(JSON.stringify(errorPayload));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求体中提取系统提示词。
|
||||||
|
* @param {Object} requestBody - 请求体对象。
|
||||||
|
* @param {string} provider - 提供商类型('openai', 'gemini', 'claude')。
|
||||||
|
* @returns {string} 提取到的系统提示词字符串。
|
||||||
|
*/
|
||||||
|
export function extractSystemPromptFromRequestBody(requestBody, provider) {
|
||||||
|
let incomingSystemText = '';
|
||||||
|
switch (provider) {
|
||||||
|
case MODEL_PROTOCOL_PREFIX.OPENAI:
|
||||||
|
const openaiSystemMessage = requestBody.messages?.find(m => m.role === 'system');
|
||||||
|
if (openaiSystemMessage?.content) {
|
||||||
|
incomingSystemText = openaiSystemMessage.content;
|
||||||
|
} else if (requestBody.messages?.length > 0) {
|
||||||
|
// Fallback to first user message if no system message
|
||||||
|
const userMessage = requestBody.messages.find(m => m.role === 'user');
|
||||||
|
if (userMessage) {
|
||||||
|
incomingSystemText = userMessage.content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MODEL_PROTOCOL_PREFIX.GEMINI:
|
||||||
|
const geminiSystemInstruction = requestBody.system_instruction || requestBody.systemInstruction;
|
||||||
|
if (geminiSystemInstruction?.parts) {
|
||||||
|
incomingSystemText = geminiSystemInstruction.parts
|
||||||
|
.filter(p => p?.text)
|
||||||
|
.map(p => p.text)
|
||||||
|
.join('\n');
|
||||||
|
} else if (requestBody.contents?.length > 0) {
|
||||||
|
// Fallback to first user content if no system instruction
|
||||||
|
const userContent = requestBody.contents[0];
|
||||||
|
if (userContent?.parts) {
|
||||||
|
incomingSystemText = userContent.parts
|
||||||
|
.filter(p => p?.text)
|
||||||
|
.map(p => p.text)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MODEL_PROTOCOL_PREFIX.CLAUDE:
|
||||||
|
if (typeof requestBody.system === 'string') {
|
||||||
|
incomingSystemText = requestBody.system;
|
||||||
|
} else if (typeof requestBody.system === 'object') {
|
||||||
|
incomingSystemText = JSON.stringify(requestBody.system);
|
||||||
|
} else if (requestBody.messages?.length > 0) {
|
||||||
|
// Fallback to first user message if no system property
|
||||||
|
const userMessage = requestBody.messages.find(m => m.role === 'user');
|
||||||
|
if (userMessage) {
|
||||||
|
if (Array.isArray(userMessage.content)) {
|
||||||
|
incomingSystemText = userMessage.content.map(block => block.text).join('');
|
||||||
|
} else {
|
||||||
|
incomingSystemText = userMessage.content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(`[System Prompt] Unknown provider: ${provider}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return incomingSystemText;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { API_ACTIONS } from '../common.js';
|
import { API_ACTIONS, extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../common.js';
|
||||||
import { ProviderStrategy } from '../provider-strategy.js';
|
import { ProviderStrategy } from '../provider-strategy.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,14 +46,7 @@ class GeminiStrategy extends ProviderStrategy {
|
||||||
return requestBody;
|
return requestBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
let existingSystemText = '';
|
const existingSystemText = extractSystemPromptFromRequestBody(requestBody, MODEL_PROTOCOL_PREFIX.GEMINI);
|
||||||
const currentSystemInstruction = requestBody.system_instruction || requestBody.systemInstruction;
|
|
||||||
if (currentSystemInstruction?.parts) {
|
|
||||||
existingSystemText = currentSystemInstruction.parts
|
|
||||||
.filter(p => p?.text)
|
|
||||||
.map(p => p.text)
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append' && existingSystemText
|
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append' && existingSystemText
|
||||||
? `${existingSystemText}\n${filePromptContent}`
|
? `${existingSystemText}\n${filePromptContent}`
|
||||||
|
|
@ -69,15 +62,8 @@ class GeminiStrategy extends ProviderStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
async manageSystemPrompt(requestBody) {
|
async manageSystemPrompt(requestBody) {
|
||||||
let incomingSystemText = '';
|
const incomingSystemText = extractSystemPromptFromRequestBody(requestBody, MODEL_PROTOCOL_PREFIX.GEMINI);
|
||||||
const geminiSystemInstruction = requestBody.system_instruction || requestBody.systemInstruction;
|
await this._updateSystemPromptFile(incomingSystemText, MODEL_PROTOCOL_PREFIX.GEMINI);
|
||||||
if (geminiSystemInstruction?.parts) {
|
|
||||||
incomingSystemText = geminiSystemInstruction.parts
|
|
||||||
.filter(p => p?.text)
|
|
||||||
.map(p => p.text)
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
await this._updateSystemPromptFile(incomingSystemText, 'gemini');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { ProviderStrategy } from '../provider-strategy.js';
|
import { ProviderStrategy } from '../provider-strategy.js';
|
||||||
|
import { extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../common.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenAI provider strategy implementation.
|
* OpenAI provider strategy implementation.
|
||||||
|
|
@ -51,11 +52,7 @@ class OpenAIStrategy extends ProviderStrategy {
|
||||||
return requestBody;
|
return requestBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
let existingSystemText = '';
|
const existingSystemText = extractSystemPromptFromRequestBody(requestBody, MODEL_PROTOCOL_PREFIX.OPENAI);
|
||||||
const systemMessage = requestBody.messages?.find(m => m.role === 'system');
|
|
||||||
if (systemMessage) {
|
|
||||||
existingSystemText = systemMessage.content || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append' && existingSystemText
|
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append' && existingSystemText
|
||||||
? `${existingSystemText}\n${filePromptContent}`
|
? `${existingSystemText}\n${filePromptContent}`
|
||||||
|
|
@ -77,15 +74,8 @@ class OpenAIStrategy extends ProviderStrategy {
|
||||||
|
|
||||||
async manageSystemPrompt(requestBody) {
|
async manageSystemPrompt(requestBody) {
|
||||||
//console.log('[System Prompt] Managing system prompt for provider "openai".', requestBody);
|
//console.log('[System Prompt] Managing system prompt for provider "openai".', requestBody);
|
||||||
let incomingSystemText = '';
|
const incomingSystemText = extractSystemPromptFromRequestBody(requestBody, MODEL_PROTOCOL_PREFIX.OPENAI);
|
||||||
const systemMessage = requestBody.messages?.find(m => m.role === 'system');
|
await this._updateSystemPromptFile(incomingSystemText, MODEL_PROTOCOL_PREFIX.OPENAI);
|
||||||
if (systemMessage?.content) {
|
|
||||||
incomingSystemText = systemMessage.content;
|
|
||||||
}
|
|
||||||
if (!incomingSystemText) {
|
|
||||||
incomingSystemText = requestBody.messages.filter(m => m.role === 'user')[0].content;
|
|
||||||
}
|
|
||||||
await this._updateSystemPromptFile(incomingSystemText, 'openai');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue