mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-11 08:19:01 +08:00
feat(agents): refactor agent invocation to use session object and enhance error handling
This commit is contained in:
parent
1c2211aefb
commit
1515f511a1
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import { EventEmitter } from 'node:events'
|
import { EventEmitter } from 'node:events'
|
||||||
|
|
||||||
|
import { GetAgentSessionResponse } from '@types'
|
||||||
import { UIMessageChunk } from 'ai'
|
import { UIMessageChunk } from 'ai'
|
||||||
|
|
||||||
// Generic agent stream event that works with any agent type
|
// Generic agent stream event that works with any agent type
|
||||||
@ -23,5 +24,5 @@ export interface AgentStream extends EventEmitter {
|
|||||||
|
|
||||||
// Base agent service interface
|
// Base agent service interface
|
||||||
export interface AgentServiceInterface {
|
export interface AgentServiceInterface {
|
||||||
invoke(prompt: string, cwd: string, sessionId?: string, options?: any): AgentStream
|
invoke(prompt: string, session: GetAgentSessionResponse, lastAgentSessionId?: string): Promise<AgentStream>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -198,10 +198,7 @@ export class SessionMessageService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the streaming agent invocation (using invokeStream for streaming)
|
// Create the streaming agent invocation (using invokeStream for streaming)
|
||||||
const claudeStream = this.cc.invoke(req.content, session.accessible_paths[0], agentSessionId, {
|
const claudeStream = await this.cc.invoke(req.content, session, agentSessionId)
|
||||||
permissionMode: session.configuration?.permission_mode,
|
|
||||||
maxTurns: session.configuration?.max_turns
|
|
||||||
})
|
|
||||||
|
|
||||||
// Use chunk accumulator to manage streaming data
|
// Use chunk accumulator to manage streaming data
|
||||||
const accumulator = new ChunkAccumulator()
|
const accumulator = new ChunkAccumulator()
|
||||||
|
|||||||
@ -4,7 +4,10 @@ import { createRequire } from 'node:module'
|
|||||||
|
|
||||||
import { Options, query, SDKMessage } from '@anthropic-ai/claude-code'
|
import { Options, query, SDKMessage } from '@anthropic-ai/claude-code'
|
||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
|
// import { config as apiConfig } from '@main/apiServer/config'
|
||||||
|
import { validateModelId } from '@main/apiServer/utils'
|
||||||
|
|
||||||
|
import { GetAgentSessionResponse } from '../..'
|
||||||
import { AgentServiceInterface, AgentStream, AgentStreamEvent } from '../../interfaces/AgentStreamInterface'
|
import { AgentServiceInterface, AgentStream, AgentStreamEvent } from '../../interfaces/AgentStreamInterface'
|
||||||
import { transformSDKMessageToUIChunk } from './transform'
|
import { transformSDKMessageToUIChunk } from './transform'
|
||||||
|
|
||||||
@ -34,9 +37,45 @@ class ClaudeCodeService implements AgentServiceInterface {
|
|||||||
this.claudeExecutablePath = require_.resolve('@anthropic-ai/claude-code/cli.js')
|
this.claudeExecutablePath = require_.resolve('@anthropic-ai/claude-code/cli.js')
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke(prompt: string, cwd: string, session_id?: string, base?: Options): AgentStream {
|
async invoke(prompt: string, session: GetAgentSessionResponse, lastAgentSessionId?: string): Promise<AgentStream> {
|
||||||
const aiStream = new ClaudeCodeStream()
|
const aiStream = new ClaudeCodeStream()
|
||||||
|
|
||||||
|
// Validate session accessible paths and make sure it exists as a directory
|
||||||
|
const cwd = session.accessible_paths[0]
|
||||||
|
if (!cwd) {
|
||||||
|
aiStream.emit('data', {
|
||||||
|
type: 'error',
|
||||||
|
error: new Error('No accessible paths defined for the agent session')
|
||||||
|
})
|
||||||
|
return aiStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate model
|
||||||
|
const modelId = session.model
|
||||||
|
logger.info('Invoking Claude Code with model', { modelId, cwd })
|
||||||
|
const modelInfo = await validateModelId(modelId)
|
||||||
|
if (!modelInfo.valid) {
|
||||||
|
aiStream.emit('data', {
|
||||||
|
type: 'error',
|
||||||
|
error: new Error(`Invalid model ID '${modelId}': ${JSON.stringify(modelInfo.error)}`)
|
||||||
|
})
|
||||||
|
return aiStream
|
||||||
|
}
|
||||||
|
if (modelInfo.provider?.type !== 'anthropic' || modelInfo.provider.apiKey === '') {
|
||||||
|
aiStream.emit('data', {
|
||||||
|
type: 'error',
|
||||||
|
error: new Error(`Invalid provider type '${modelInfo.provider?.type}'. Expected 'anthropic' provider type.`)
|
||||||
|
})
|
||||||
|
return aiStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use cherry studio api server config instead of direct provider config to provide more flexibility (e.g. custom headers, proxy, statistics, etc).
|
||||||
|
// const cfg = await apiConfig.get()
|
||||||
|
// process.env.ANTHROPIC_AUTH_TOKEN = cfg.apiKey
|
||||||
|
// process.env.ANTHROPIC_BASE_URL = `http://${cfg.host}:${cfg.port}`
|
||||||
|
process.env.ANTHROPIC_AUTH_TOKEN = modelInfo.provider.apiKey
|
||||||
|
process.env.ANTHROPIC_BASE_URL = modelInfo.provider.apiHost
|
||||||
|
|
||||||
// Build SDK options from parameters
|
// Build SDK options from parameters
|
||||||
const options: Options = {
|
const options: Options = {
|
||||||
cwd,
|
cwd,
|
||||||
@ -44,11 +83,12 @@ class ClaudeCodeService implements AgentServiceInterface {
|
|||||||
stderr: (chunk: string) => {
|
stderr: (chunk: string) => {
|
||||||
logger.info('claude stderr', { chunk })
|
logger.info('claude stderr', { chunk })
|
||||||
},
|
},
|
||||||
...base
|
permissionMode: session.configuration?.permission_mode,
|
||||||
|
maxTurns: session.configuration?.max_turns
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session_id) {
|
if (lastAgentSessionId) {
|
||||||
options.resume = session_id
|
options.resume = lastAgentSessionId
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Starting Claude Code SDK query', {
|
logger.info('Starting Claude Code SDK query', {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user