feat(queue): implement queue scheduler service and API routes

- Added QueueSchedulerService to manage task queue lifecycle, including state machine, dependency resolution, and session management.
- Implemented HTTP API endpoints for queue scheduling:
  - POST /api/queue/execute: Submit items to the scheduler.
  - GET /api/queue/scheduler/state: Retrieve full scheduler state.
  - POST /api/queue/scheduler/start: Start scheduling loop with items.
  - POST /api/queue/scheduler/pause: Pause scheduling.
  - POST /api/queue/scheduler/stop: Graceful stop of the scheduler.
  - POST /api/queue/scheduler/config: Update scheduler configuration.
- Introduced types for queue items, scheduler state, and WebSocket messages to ensure type safety and compatibility with the backend.
- Added static model lists for LiteLLM as a fallback for available models.
This commit is contained in:
catlog22
2026-02-27 20:53:46 +08:00
parent 5b54f38aa3
commit 75173312c1
47 changed files with 3813 additions and 307 deletions

View File

@@ -236,13 +236,18 @@ function parseGeminiQwenSession(content: string, tool: string): ParsedSession {
let model: string | undefined;
for (const msg of session.messages) {
// Ensure content is always a string (handle legacy object data like {text: "..."})
const contentStr = typeof msg.content === 'string'
? msg.content
: JSON.stringify(msg.content);
if (msg.type === 'user') {
turnNumber++;
turns.push({
turnNumber,
timestamp: msg.timestamp,
role: 'user',
content: msg.content
content: contentStr
});
} else if (msg.type === 'gemini' || msg.type === 'qwen') {
// Find the corresponding user turn
@@ -255,7 +260,7 @@ function parseGeminiQwenSession(content: string, tool: string): ParsedSession {
turnNumber,
timestamp: msg.timestamp,
role: 'assistant',
content: msg.content,
content: contentStr,
thoughts: thoughts.length > 0 ? thoughts : undefined,
tokens: msg.tokens ? {
input: msg.tokens.input,
@@ -428,7 +433,11 @@ function parseCodexSession(content: string): ParsedSession {
currentTurn++;
const textContent = item.payload.content
?.filter(c => c.type === 'input_text')
.map(c => c.text)
.map(c => {
// Ensure text is a string (handle legacy object data like {text: "..."})
const txt = c.text;
return typeof txt === 'string' ? txt : JSON.stringify(txt);
})
.join('\n') || '';
turns.push({
@@ -461,7 +470,11 @@ function parseCodexSession(content: string): ParsedSession {
// Assistant message (final response)
const textContent = item.payload.content
?.filter(c => c.type === 'output_text' || c.type === 'text')
.map(c => c.text)
.map(c => {
// Ensure text is a string (handle legacy object data like {text: "..."})
const txt = c.text;
return typeof txt === 'string' ? txt : JSON.stringify(txt);
})
.join('\n') || '';
if (textContent) {