Skip to content

Client 端插件

创建插件

typescript
const myPlugin = {
  hooks: {
    beforeCall: async (ctx) => { ... },
    afterCall: async (ctx, stream) => { return stream },
  }
}

client.use(myPlugin)

Hooks

beforeCall

调用技能前:

typescript
{
  beforeCall: async (ctx) => {
    // 注入认证
    ctx.metadata['authorization'] = `Bearer ${token}`

    // 注入追踪 ID
    ctx.metadata['x-trace-id'] = generateTraceId()

    console.log('Calling:', ctx.skill)
  }
}

afterCall

调用完成后(必须返回 stream):

typescript
{
  afterCall: async (ctx, stream) => {
    console.log('Connected to', ctx.skill)
    return stream
  }
}

onError

错误处理:

typescript
{
  onError: async (ctx, error) => {
    console.error('Failed:', ctx.skill, error.message)
    await reportError(error)
  }
}

常用插件示例

日志插件

typescript
const createLoggingPlugin = () => ({
  hooks: {
    beforeCall: async (ctx) => {
      console.log('[Client] Calling', ctx.skill)
      ctx.startTime = Date.now()
    },

    afterCall: async (ctx, stream) => {
      console.log('[Client] Connected to', ctx.skill)
      return stream
    },

    onError: async (ctx, error) => {
      const duration = Date.now() - ctx.startTime
      console.error(`[Client] ${ctx.skill} failed after ${duration}ms:`, error)
    }
  }
})

认证插件

typescript
const createAuthPlugin = (getToken: () => Promise<string>) => ({
  hooks: {
    beforeCall: async (ctx) => {
      const token = await getToken()
      ctx.metadata['authorization'] = `Bearer ${token}`
    }
  }
})

// 使用
client.use(createAuthPlugin(async () => {
  return localStorage.getItem('token')
}))

追踪插件

typescript
const createTracingPlugin = (serviceName: string) => ({
  hooks: {
    beforeCall: async (ctx) => {
      const traceId = ctx.metadata['x-trace-id'] || generateTraceId()
      const spanId = generateSpanId()

      ctx.metadata['x-trace-id'] = traceId
      ctx.metadata['x-span-id'] = spanId
      ctx.traceInfo = { traceId, spanId, startTime: Date.now() }
    },

    afterCall: async (ctx, stream) => {
      const { traceId, spanId, startTime } = ctx.traceInfo

      // 包装 stream 记录完成时间
      const originalIterator = stream[Symbol.asyncIterator].bind(stream)
      stream[Symbol.asyncIterator] = async function* () {
        try {
          for await (const msg of originalIterator()) {
            yield msg
          }
        } finally {
          reportSpan({
            service: serviceName,
            traceId,
            spanId,
            skill: ctx.skill,
            duration: Date.now() - startTime
          })
        }
      }

      return stream
    }
  }
})

组合多个插件

typescript
const client = createAgentClient(config)
  .use(createLoggingPlugin())
  .use(createAuthPlugin(getToken))
  .use(createTracingPlugin('web-client'))

// 执行顺序:
// beforeCall: logging → auth → tracing
// afterCall: logging → auth → tracing
// onError: logging → auth → tracing

浏览器使用

typescript
import { createAgentClient } from '@multi-agent/a2a/browser'

const client = createAgentClient(config)
  .use(createLoggingPlugin())
  .use(createAuthPlugin(getToken))

MIT Licensed