feat(agents): refactor agent invocation to use session object and enhance error handling

This commit is contained in:
Vaayne 2025-09-19 19:15:35 +08:00
parent 1c2211aefb
commit 1515f511a1
3 changed files with 47 additions and 9 deletions

View File

@ -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>
} }

View File

@ -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()

View File

@ -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', {