diff --git a/examples/agent-runtime.ts b/examples/agent-runtime.ts index 0e6d872..85e9754 100644 --- a/examples/agent-runtime.ts +++ b/examples/agent-runtime.ts @@ -88,12 +88,12 @@ async function createOrGetAgentRuntime(): Promise { codeConfiguration: await codeFromFile( AgentRuntimeLanguage.NODEJS18, ['node', 'index.js'], - codePath, + codePath ), port: 9000, cpu: 2, memory: 4096, - } + }, }); log(`创建成功 / Created successfully: ${ar.agentRuntimeId}`); @@ -113,10 +113,10 @@ async function createOrGetAgentRuntime(): Promise { ar.status === Status.DELETE_FAILED ) { log( - `已存在的 Agent Runtime 处于失败状态: ${ar.status}, 删除并重新创建 / Existing Agent Runtime is in failed state: ${ar.status}, deleting and recreating`, + `已存在的 Agent Runtime 处于失败状态: ${ar.status}, 删除并重新创建 / Existing Agent Runtime is in failed state: ${ar.status}, deleting and recreating` ); await ar.delete(); - + // Wait for deletion to complete log('等待删除完成 / Waiting for deletion to complete...'); let deleted = false; @@ -134,7 +134,7 @@ async function createOrGetAgentRuntime(): Promise { break; } } - + if (!deleted) { throw new Error('等待删除超时 / Deletion timeout'); } @@ -155,12 +155,12 @@ async function createOrGetAgentRuntime(): Promise { codeConfiguration: await codeFromFile( AgentRuntimeLanguage.NODEJS18, ['node', 'index.js'], - codePath, + codePath ), port: 9000, cpu: 2, memory: 4096, - } + }, }); log(`创建成功 / Created successfully: ${ar.agentRuntimeId}`); } @@ -168,7 +168,8 @@ async function createOrGetAgentRuntime(): Promise { // Wait for ready or failed log('等待就绪 / Waiting for ready...'); await ar.waitUntilReadyOrFailed({ - beforeCheck: (runtime) => log(` 当前状态 / Current status: ${runtime.status}`), + callback: (runtime) => + log(` 当前状态 / Current status: ${runtime.status}`), }); if (ar.status !== Status.READY) { @@ -196,7 +197,8 @@ async function updateAgentRuntime(ar: AgentRuntime): Promise { }); await ar.waitUntilReadyOrFailed({ - beforeCheck: (runtime) => log(` 当前状态 / Current status: ${runtime.status}`), + callback: (runtime) => + log(` 当前状态 / Current status: ${runtime.status}`), }); if (ar.status !== Status.READY) { @@ -213,10 +215,10 @@ async function updateAgentRuntime(ar: AgentRuntime): Promise { async function listAgentRuntimes(): Promise { log('枚举资源列表 / Listing resources'); - const runtimes = await client.listAll(); + const runtimes = await AgentRuntime.listAll(); log( `共有 ${runtimes.length} 个资源 / Total ${runtimes.length} resources:`, - runtimes.map((r) => r.agentRuntimeName), + runtimes.map((r) => r.agentRuntimeName) ); } @@ -232,8 +234,9 @@ async function deleteAgentRuntime(ar: AgentRuntime): Promise { // Wait for deletion log('等待删除完成 / Waiting for deletion...'); try { - await ar.waitUntilReady({ - beforeCheck: (runtime) => log(` 当前状态 / Current status: ${runtime.status}`), + await ar.waitUntilReadyOrFailed({ + callback: (runtime) => + log(` 当前状态 / Current status: ${runtime.status}`), }); } catch (error) { // Expected to fail when resource is deleted @@ -245,7 +248,9 @@ async function deleteAgentRuntime(ar: AgentRuntime): Promise { log('资源仍然存在 / Resource still exists'); } catch (error) { if (error instanceof ResourceNotExistError) { - log('得到资源不存在报错,删除成功 / Resource not found, deletion successful'); + log( + '得到资源不存在报错,删除成功 / Resource not found, deletion successful' + ); } else { throw error; } diff --git a/examples/model.ts b/examples/model.ts index 2ddcc89..6a6c549 100644 --- a/examples/model.ts +++ b/examples/model.ts @@ -67,8 +67,9 @@ async function createOrGetModelService(): Promise { } // 等待就绪 / Wait for ready - await ms.waitUntilReady({ - beforeCheck: (service: ModelService) => log(` 当前状态 / Current status: ${service.status}`), + await ms.waitUntilReadyOrFailed({ + beforeCheck: (service: ModelService) => + log(` 当前状态 / Current status: ${service.status}`), }); if (ms.status !== Status.READY) { @@ -95,7 +96,7 @@ async function updateModelService(ms: ModelService): Promise { }, }); - await ms.waitUntilReady(); + await ms.waitUntilReadyOrFailed(); if (ms.status !== Status.READY) { throw new Error(`状态异常 / Unexpected status: ${ms.status}`); @@ -124,7 +125,7 @@ async function listModelServices(): Promise { async function invokeModelService(ms: ModelService): Promise { log('调用模型服务进行推理 / Invoking model service for inference'); - const result = await ms.completions({ + const result = await ms.completion({ messages: [{ role: 'user', content: '你好,请介绍一下你自己' }], stream: true, }); @@ -196,8 +197,9 @@ async function createOrGetModelProxy(): Promise { } // 等待就绪 / Wait for ready - await mp.waitUntilReady({ - beforeCheck: (proxy: ModelProxy) => log(` 当前状态 / Current status: ${proxy.status}`), + await mp.waitUntilReadyOrFailed({ + beforeCheck: (proxy: ModelProxy) => + log(` 当前状态 / Current status: ${proxy.status}`), }); if (mp.status !== Status.READY) { @@ -226,7 +228,7 @@ async function updateModelProxy(mp: ModelProxy): Promise { }, }); - await mp.waitUntilReady(); + await mp.waitUntilReadyOrFailed(); if (mp.status !== Status.READY) { throw new Error(`状态异常 / Unexpected status: ${mp.status}`); diff --git a/examples/sandbox.ts b/examples/sandbox.ts index e3a6855..d213a89 100644 --- a/examples/sandbox.ts +++ b/examples/sandbox.ts @@ -13,11 +13,18 @@ * bun run examples/sandbox.ts */ -import * as fs from "fs/promises"; -import * as path from "path"; - -import { CodeInterpreterSandbox, CodeLanguage, Sandbox, SandboxClient, Template, TemplateType } from "../src/index"; -import { logger } from "../src/utils/log"; +import * as fs from 'fs/promises'; +import * as path from 'path'; + +import { + CodeInterpreterSandbox, + CodeLanguage, + Sandbox, + SandboxClient, + Template, + TemplateType, +} from '../src/index'; +import { logger } from '../src/utils/log'; // Logger helper function log(message: string, ...args: unknown[]) { @@ -30,13 +37,15 @@ const client = new SandboxClient(); * 列出模板 / List templates */ async function listTemplates(): Promise { - log("枚举模板列表 / Listing templates"); + log('枚举模板列表 / Listing templates'); const templates = await client.listAllTemplates(); log(`共有 ${templates.length} 个模板 / Total ${templates.length} templates:`); for (const template of templates) { - log(` - ${template.templateName} (${template.templateType}) [${template.status}]`); + log( + ` - ${template.templateName} (${template.templateType}) [${template.status}]` + ); } } @@ -44,7 +53,7 @@ async function listTemplates(): Promise { * 列出沙箱 / List sandboxes */ async function listSandboxes(): Promise { - log("枚举沙箱列表 / Listing sandboxes"); + log('枚举沙箱列表 / Listing sandboxes'); const sandboxes = await client.listSandboxes(); log(`共有 ${sandboxes.length} 个沙箱 / Total ${sandboxes.length} sandboxes:`); @@ -58,18 +67,18 @@ async function listSandboxes(): Promise { * Code Interpreter 测试 / Code Interpreter test */ async function codeInterpreterExample(): Promise { - log("=".repeat(60)); - log("开始测试 Code Interpreter / Starting Code Interpreter test"); - log("=".repeat(60)); + log('='.repeat(60)); + log('开始测试 Code Interpreter / Starting Code Interpreter test'); + log('='.repeat(60)); const templateName = `sdk-nodejs-template-${Date.now()}`; // 创建模板 / Create template - log("\n--- 创建模板 / Creating template ---"); + log('\n--- 创建模板 / Creating template ---'); const template = await Template.create({ templateName, templateType: TemplateType.CODE_INTERPRETER, - description: "Test template from Node.js SDK", + description: 'Test template from Node.js SDK', sandboxIdleTimeoutInSeconds: 600, }); @@ -78,35 +87,39 @@ async function codeInterpreterExample(): Promise { log(` - 模板状态: ${template.status}`); // 等待模板就绪 / Wait for template to be ready - log("\n--- 等待模板就绪 / Waiting for template to be ready ---"); - await template.waitUntilReady({ - beforeCheck: (t) => log(` 当前状态 / Current status: ${t.status}`), + log('\n--- 等待模板就绪 / Waiting for template to be ready ---'); + await template.waitUntilReadyOrFailed({ + callback: (t) => log(` 当前状态 / Current status: ${t.status}`), }); - log("✓ 模板已就绪 / Template is ready"); + log('✓ 模板已就绪 / Template is ready'); // 创建沙箱 / Create sandbox - log("\n--- 创建 Code Interpreter 沙箱 / Creating Code Interpreter sandbox ---"); + log( + '\n--- 创建 Code Interpreter 沙箱 / Creating Code Interpreter sandbox ---' + ); const sandbox = await CodeInterpreterSandbox.createFromTemplate(templateName); log(`✓ 创建沙箱成功 / Sandbox created: ${sandbox.sandboxId}`); // 等待沙箱运行 / Wait for sandbox to be running - log("\n--- 等待沙箱运行 / Waiting for sandbox to be running ---"); + log('\n--- 等待沙箱运行 / Waiting for sandbox to be running ---'); await sandbox.waitUntilRunning({ beforeCheck: (s) => log(` 当前状态 / Current state: ${s.state}`), }); - log("✓ 沙箱已运行 / Sandbox is running"); + log('✓ 沙箱已运行 / Sandbox is running'); // 等待沙箱健康检查通过 - log("\n--- 等待沙箱就绪 / Waiting for sandbox to be ready ---"); - await sandbox.waitUntilReady(); - log("✓ 沙箱健康检查通过 / Sandbox is healthy"); + log('\n--- 等待沙箱就绪 / Waiting for sandbox to be ready ---'); + await sandbox.waitUntilReadyOrFailed(); + log('✓ 沙箱健康检查通过 / Sandbox is healthy'); // 测试代码执行上下文 - log("\n--- 测试代码执行上下文 / Testing code execution context ---"); + log('\n--- 测试代码执行上下文 / Testing code execution context ---'); const ctx = await sandbox.context.create({ language: CodeLanguage.PYTHON }); log(`✓ 创建上下文成功 / Context created: ${ctx.contextId}`); - const execResult = await ctx.execute({ code: "print('Hello from Node.js SDK!')" }); + const execResult = await ctx.execute({ + code: "print('Hello from Node.js SDK!')", + }); log(`✓ 执行代码结果 / Code execution result:`, execResult); const contexts = await ctx.list(); @@ -116,22 +129,24 @@ async function codeInterpreterExample(): Promise { log(`✓ 获取上下文详情 / Context details: ${contextDetails.contextId}`); // 测试文件系统操作 / File system operations - log("\n--- 测试文件系统操作 / Testing file system operations ---"); - const rootFiles = await sandbox.fileSystem.list({ path: "/" }); + log('\n--- 测试文件系统操作 / Testing file system operations ---'); + const rootFiles = await sandbox.fileSystem.list({ path: '/' }); log(`✓ 根目录文件列表 / Root directory listing:`, rootFiles); - await sandbox.fileSystem.mkdir({ path: "/home/user/test" }); + await sandbox.fileSystem.mkdir({ path: '/home/user/test' }); log(`✓ 创建文件夹 /home/user/test / Created directory /home/user/test`); - await sandbox.fileSystem.mkdir({ path: "/home/user/test-move" }); - log(`✓ 创建文件夹 /home/user/test-move / Created directory /home/user/test-move`); + await sandbox.fileSystem.mkdir({ path: '/home/user/test-move' }); + log( + `✓ 创建文件夹 /home/user/test-move / Created directory /home/user/test-move` + ); // 测试上传下载 / Upload/Download test - log("\n--- 测试上传下载 / Testing upload/download ---"); - const testFilePath = "./temp_test_file.txt"; + log('\n--- 测试上传下载 / Testing upload/download ---'); + const testFilePath = './temp_test_file.txt'; const testContent = - "这是一个测试文件,用于验证 Sandbox 文件上传下载功能。\n" + - "This is a test file for validating Sandbox file upload/download.\n" + + '这是一个测试文件,用于验证 Sandbox 文件上传下载功能。\n' + + 'This is a test file for validating Sandbox file upload/download.\n' + `创建时间 / Created at: ${new Date().toISOString()}\n`; await fs.writeFile(testFilePath, testContent); @@ -139,62 +154,74 @@ async function codeInterpreterExample(): Promise { await sandbox.fileSystem.upload({ localFilePath: testFilePath, - targetFilePath: "/home/user/test-move/test_file.txt", + targetFilePath: '/home/user/test-move/test_file.txt', }); log(`✓ 上传文件成功 / File uploaded successfully`); - const filestat = await sandbox.fileSystem.stat("/home/user/test-move/test_file.txt"); + const filestat = await sandbox.fileSystem.stat( + '/home/user/test-move/test_file.txt' + ); log(`✓ 上传文件详情 / Uploaded file stat:`, filestat); - const downloadPath = "./downloaded_test_file.txt"; + const downloadPath = './downloaded_test_file.txt'; const downloadResult = await sandbox.fileSystem.download({ - path: "/home/user/test-move/test_file.txt", + path: '/home/user/test-move/test_file.txt', savePath: downloadPath, }); log(`✓ 下载文件结果 / Downloaded file:`, downloadResult); // 验证下载的文件内容 - const downloadedContent = await fs.readFile(downloadPath, "utf-8"); - log(`✓ 验证下载文件内容 / Verify downloaded content: ${downloadedContent.slice(0, 50)}...`); + const downloadedContent = await fs.readFile(downloadPath, 'utf-8'); + log( + `✓ 验证下载文件内容 / Verify downloaded content: ${downloadedContent.slice( + 0, + 50 + )}...` + ); // 测试文件读写 / File read/write test - log("\n--- 测试文件读写 / Testing file read/write ---"); - await sandbox.file.write({ path: "/home/user/test/test.txt", content: "hello world" }); + log('\n--- 测试文件读写 / Testing file read/write ---'); + await sandbox.file.write({ + path: '/home/user/test/test.txt', + content: 'hello world', + }); log(`✓ 写入文件成功 / File written successfully`); - const readResult = await sandbox.file.read("/home/user/test/test.txt"); + const readResult = await sandbox.file.read('/home/user/test/test.txt'); log(`✓ 读取文件结果 / File read result:`, readResult); // 测试文件移动 / File move test - log("\n--- 测试文件移动 / Testing file move ---"); + log('\n--- 测试文件移动 / Testing file move ---'); await sandbox.fileSystem.move({ - source: "/home/user/test/test.txt", - destination: "/home/user/test-move/test2.txt", + source: '/home/user/test/test.txt', + destination: '/home/user/test-move/test2.txt', }); log(`✓ 移动文件成功 / File moved successfully`); - const movedContent = await sandbox.file.read("/home/user/test-move/test2.txt"); + const movedContent = await sandbox.file.read( + '/home/user/test-move/test2.txt' + ); log(`✓ 读取移动后的文件 / Read moved file:`, movedContent); // 测试文件详情 / File stat test - log("\n--- 测试文件详情 / Testing file stat ---"); - const dirStat = await sandbox.fileSystem.stat("/home/user/test-move"); + log('\n--- 测试文件详情 / Testing file stat ---'); + const dirStat = await sandbox.fileSystem.stat('/home/user/test-move'); log(`✓ 文件详情 / File stat:`, dirStat); // 测试删除文件 / Delete test - log("\n--- 测试删除文件 / Testing file deletion ---"); - await sandbox.fileSystem.remove("/home/user/test-move"); + log('\n--- 测试删除文件 / Testing file deletion ---'); + await sandbox.fileSystem.remove('/home/user/test-move'); log(`✓ 删除文件夹成功 / Directory deleted successfully`); // 测试进程操作 / Process operations - log("\n--- 测试进程操作 / Testing process operations ---"); + log('\n--- 测试进程操作 / Testing process operations ---'); const processes = await sandbox.process.list(); log(`✓ 进程列表 / Process list:`, processes); - const cmdResult = await sandbox.process.cmd({ command: "ls", cwd: "/" }); + const cmdResult = await sandbox.process.cmd({ command: 'ls', cwd: '/' }); log(`✓ 进程执行结果 / Process execution result:`, cmdResult); - const processDetails = await sandbox.process.get("1"); + const processDetails = await sandbox.process.get('1'); log(`✓ 进程详情 / Process details:`, processDetails); // 清理上下文 @@ -202,18 +229,18 @@ async function codeInterpreterExample(): Promise { log(`✓ 删除上下文成功 / Context deleted`); // 停止沙箱 / Stop sandbox - log("\n--- 停止沙箱 / Stopping sandbox ---"); + log('\n--- 停止沙箱 / Stopping sandbox ---'); await sandbox.stop(); - log("✓ 沙箱已停止 / Sandbox stopped"); + log('✓ 沙箱已停止 / Sandbox stopped'); // 清理资源 / Cleanup - log("\n--- 清理资源 / Cleaning up ---"); + log('\n--- 清理资源 / Cleaning up ---'); await sandbox.delete(); - log("✓ 沙箱已删除 / Sandbox deleted"); + log('✓ 沙箱已删除 / Sandbox deleted'); await template.delete(); - log("✓ 模板已删除 / Template deleted"); + log('✓ 模板已删除 / Template deleted'); // 清理临时测试文件 / Clean up temp files try { @@ -230,14 +257,14 @@ async function codeInterpreterExample(): Promise { // Ignore if file doesn't exist } - log("\n✓ Code Interpreter 测试完成 / Code Interpreter test complete\n"); + log('\n✓ Code Interpreter 测试完成 / Code Interpreter test complete\n'); } /** * 主函数 / Main function */ async function main() { - log("==== 沙箱模块基本功能示例 / Sandbox Module Example ===="); + log('==== 沙箱模块基本功能示例 / Sandbox Module Example ===='); try { // List existing templates and sandboxes @@ -251,9 +278,9 @@ async function main() { await listTemplates(); await listSandboxes(); - log("==== 示例完成 / Example Complete ===="); + log('==== 示例完成 / Example Complete ===='); } catch (error) { - logger.error("Error:", error); + logger.error('Error:', error); process.exit(1); } } diff --git a/examples/toolset.ts b/examples/toolset.ts index 8b7a3b1..7deeafa 100644 --- a/examples/toolset.ts +++ b/examples/toolset.ts @@ -26,7 +26,7 @@ async function toolsetExample() { // Example 1: Using Baidu Search Tool (OpenAPI) logger.info('==== OpenAPI ToolSet Example ===='); try { - const baiduToolset = await client.getToolSet({ + const baiduToolset = await client.get({ name: 'web-search-baidu-8wox', // 替换为您的百度搜索工具名称 }); @@ -53,7 +53,7 @@ async function toolsetExample() { // Example 2: Using MCP Time Tool logger.info('\n==== MCP ToolSet Example ===='); try { - const mcpToolset = await client.getToolSet({ + const mcpToolset = await client.get({ name: 'start-mcp-time-ggda', // 替换为您的 MCP 时间工具名称 }); diff --git a/package.json b/package.json index 02fb171..bf26d16 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ }, "dependencies": { "@ai-sdk/openai": "^3.0.0", + "@ai-sdk/openai-compatible": "^2.0.13", "@alicloud/agentrun20250910": "^5.0.0", "@alicloud/devs20230714": "^2.4.1", "@alicloud/openapi-client": "^0.4.12", diff --git a/scripts/codegen.ts b/scripts/codegen.ts index e4fd198..7668fca 100644 --- a/scripts/codegen.ts +++ b/scripts/codegen.ts @@ -13,6 +13,10 @@ import * as fs from "fs"; import * as path from "path"; import * as yaml from "yaml"; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const projectRoot = path.resolve(__dirname, ".."); const configsDir = path.join(projectRoot, "codegen", "configs"); diff --git a/src/agent-runtime/client.ts b/src/agent-runtime/client.ts index 0ce72aa..ca01370 100644 --- a/src/agent-runtime/client.ts +++ b/src/agent-runtime/client.ts @@ -5,12 +5,16 @@ * This module provides the client API for Agent Runtime. */ -import { Config } from "../utils/config"; +import * as $AgentRun from '@alicloud/agentrun20250910'; +import { Config } from '../utils/config'; +import { HTTPError } from '../utils/exception'; +import { NetworkMode } from '../utils/model'; -import { AgentRuntimeControlAPI } from "./api/control"; -import { AgentRuntimeDataAPI, InvokeArgs } from "./api/data"; -import { AgentRuntimeEndpoint } from "./endpoint"; +import { AgentRuntimeControlAPI } from './api/control'; +import { AgentRuntimeDataAPI, InvokeArgs } from './api/data'; +import { AgentRuntimeEndpoint } from './endpoint'; import { + AgentRuntimeArtifact, AgentRuntimeCreateInput, AgentRuntimeEndpointCreateInput, AgentRuntimeEndpointListInput, @@ -19,8 +23,8 @@ import { AgentRuntimeUpdateInput, AgentRuntimeVersion, AgentRuntimeVersionListInput, -} from "./model"; -import { AgentRuntime } from "./runtime"; +} from './model'; +import { AgentRuntime } from './runtime'; /** * Agent Runtime Client @@ -45,15 +49,107 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { input, config } = params; - return AgentRuntime.create({ input, config: config ?? this.config }); + const cfg = Config.withConfigs(this.config, config); + + try { + // Set default network configuration + if (!input.networkConfiguration) { + input.networkConfiguration = {}; + } + + // Auto-detect artifact type + if (!input.artifactType) { + if (input.codeConfiguration) { + input.artifactType = AgentRuntimeArtifact.CODE; + } else if (input.containerConfiguration) { + input.artifactType = AgentRuntimeArtifact.CONTAINER; + } else { + throw new Error( + 'Either codeConfiguration or containerConfiguration must be provided' + ); + } + } + + const result = await this.controlApi.createAgentRuntime({ + input: new $AgentRun.CreateAgentRuntimeInput({ + ...input, + codeConfiguration: input.codeConfiguration + ? new $AgentRun.CodeConfiguration({ + ...input.codeConfiguration, + }) + : undefined, + containerConfiguration: input.containerConfiguration + ? new $AgentRun.ContainerConfiguration({ + ...input.containerConfiguration, + }) + : undefined, + networkConfiguration: input.networkConfiguration + ? new $AgentRun.NetworkConfiguration({ + networkMode: + input.networkConfiguration.networkMode || NetworkMode.PUBLIC, + securityGroupId: input.networkConfiguration.securityGroupId, + vpcId: input.networkConfiguration.vpcId, + vswitchIds: input.networkConfiguration.vSwitchIds, + }) + : undefined, + }), + config: cfg, + }); + + return new AgentRuntime(result, cfg); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError('AgentRuntime', input.agentRuntimeName); + } + throw error; + } }; /** * Delete an Agent Runtime */ - delete = async (params: { id: string; config?: Config }): Promise => { + delete = async (params: { + id: string; + config?: Config; + }): Promise => { const { id, config } = params; - return AgentRuntime.delete({ id, config: config ?? this.config }); + const cfg = Config.withConfigs(this.config, config); + + try { + // First delete all endpoints + const endpoints = await this.listEndpoints({ + agentRuntimeId: id, + config: cfg, + }); + for (const endpoint of endpoints) { + await endpoint.delete({ config: cfg }); + } + + // Wait for all endpoints to be deleted + let remaining = await this.listEndpoints({ + agentRuntimeId: id, + config: cfg, + }); + while (remaining.length > 0) { + await new Promise((resolve) => setTimeout(resolve, 1000)); + remaining = await this.listEndpoints({ + agentRuntimeId: id, + config: cfg, + }); + } + + const result = await this.controlApi.deleteAgentRuntime({ + agentId: id, + config: cfg, + }); + + return new AgentRuntime(result, cfg); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError('AgentRuntime', id); + } + throw error; + } }; /** @@ -65,15 +161,58 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { id, input, config } = params; - return AgentRuntime.update({ id, input, config: config ?? this.config }); + const cfg = Config.withConfigs(this.config, config); + + try { + const result = await this.controlApi.updateAgentRuntime({ + agentId: id, + input: new $AgentRun.UpdateAgentRuntimeInput({ + ...input, + codeConfiguration: input.codeConfiguration + ? new $AgentRun.CodeConfiguration({ + ...input.codeConfiguration, + }) + : undefined, + containerConfiguration: input.containerConfiguration + ? new $AgentRun.ContainerConfiguration({ + ...input.containerConfiguration, + }) + : undefined, + }), + config: cfg, + }); + + return new AgentRuntime(result, cfg); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError('AgentRuntime', id); + } + throw error; + } }; /** * Get an Agent Runtime */ - get = async (params: { id: string; config?: Config }): Promise => { + get = async (params: { + id: string; + config?: Config; + }): Promise => { const { id, config } = params; - return AgentRuntime.get({ id, config: config ?? this.config }); + const cfg = Config.withConfigs(this.config, config); + + try { + const result = await this.controlApi.getAgentRuntime({ + agentId: id, + config: cfg, + }); + return new AgentRuntime(result, cfg); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError('AgentRuntime', id); + } + throw error; + } }; /** @@ -84,23 +223,31 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { input, config } = params ?? {}; - return AgentRuntime.list(input, config ?? this.config); + const cfg = Config.withConfigs(this.config, config); + const request = new $AgentRun.ListAgentRuntimesRequest({ + ...input, + }); + const result = await this.controlApi.listAgentRuntimes({ + input: request, + config: cfg, + }); + return (result.items || []).map((item) => new AgentRuntime(item, cfg)); }; - /** - * List all Agent Runtimes (with pagination) - */ - listAll = async (params?: { - options?: { - agentRuntimeName?: string; - tags?: string; - searchMode?: string; - }; - config?: Config; - }): Promise => { - const { options, config } = params ?? {}; - return AgentRuntime.listAll(options, config ?? this.config); - }; + // /** + // * List all Agent Runtimes (with pagination) + // */ + // listAll = async (params?: { + // options?: { + // agentRuntimeName?: string; + // tags?: string; + // searchMode?: string; + // }; + // config?: Config; + // }): Promise => { + // const { options, config } = params ?? {}; + // return AgentRuntime.listAll(options, config ?? this.config); + // }; /** * Create an endpoint for an Agent Runtime @@ -111,11 +258,31 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { agentRuntimeId, input, config } = params; - return AgentRuntimeEndpoint.create({ - agentRuntimeId, - input, - config: config ?? this.config, - }); + const cfg = Config.withConfigs(this.config, config); + + try { + // Set default targetVersion to "LATEST" if not provided (same as Python SDK) + const targetVersion = input.targetVersion || 'LATEST'; + + const result = await this.controlApi.createAgentRuntimeEndpoint({ + agentId: agentRuntimeId, + input: new $AgentRun.CreateAgentRuntimeEndpointInput({ + ...input, + targetVersion, + }), + config: cfg, + }); + + return new AgentRuntimeEndpoint(result, cfg); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError( + 'AgentRuntimeEndpoint', + `${agentRuntimeId}/${input.agentRuntimeEndpointName}` + ); + } + throw error; + } }; /** @@ -127,11 +294,24 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { agentRuntimeId, endpointId, config } = params; - return AgentRuntimeEndpoint.delete({ - agentRuntimeId, - endpointId, - config: config ?? this.config, - }); + const cfg = Config.withConfigs(this.config, config); + + try { + const result = await this.controlApi.deleteAgentRuntimeEndpoint({ + agentId: agentRuntimeId, + endpointId, + config: cfg, + }); + return new AgentRuntimeEndpoint(result, cfg); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError( + 'AgentRuntimeEndpoint', + `${agentRuntimeId}/${endpointId}` + ); + } + throw error; + } }; /** @@ -144,12 +324,27 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { agentRuntimeId, endpointId, input, config } = params; - return AgentRuntimeEndpoint.update({ - agentRuntimeId, - endpointId, - input, - config: config ?? this.config, - }); + const cfg = Config.withConfigs(this.config, config); + + try { + const result = await this.controlApi.updateAgentRuntimeEndpoint({ + agentId: agentRuntimeId, + endpointId, + input: new $AgentRun.UpdateAgentRuntimeEndpointInput({ + ...input, + }), + config: cfg, + }); + return new AgentRuntimeEndpoint(result, cfg); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError( + 'AgentRuntimeEndpoint', + `${agentRuntimeId}/${endpointId}` + ); + } + throw error; + } }; /** @@ -161,11 +356,24 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { agentRuntimeId, endpointId, config } = params; - return AgentRuntimeEndpoint.get({ - agentRuntimeId, - endpointId, - config: config ?? this.config, - }); + const cfg = Config.withConfigs(this.config, config); + + try { + const result = await this.controlApi.getAgentRuntimeEndpoint({ + agentId: agentRuntimeId, + endpointId, + config: cfg, + }); + return new AgentRuntimeEndpoint(result, cfg); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError( + 'AgentRuntimeEndpoint', + `${agentRuntimeId}/${endpointId}` + ); + } + throw error; + } }; /** @@ -177,11 +385,26 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { agentRuntimeId, input, config } = params; - return AgentRuntimeEndpoint.listById({ - agentRuntimeId, - input, - config: config ?? this.config, - }); + const cfg = Config.withConfigs(this.config, config); + + try { + const request = new $AgentRun.ListAgentRuntimeEndpointsRequest({ + ...input, + }); + const result = await this.controlApi.listAgentRuntimeEndpoints({ + agentId: agentRuntimeId, + input: request, + config: cfg, + }); + return (result.items || []).map( + (item) => new AgentRuntimeEndpoint(item, cfg) + ); + } catch (error) { + if (error instanceof HTTPError) { + throw error.toResourceError('AgentRuntime', agentRuntimeId); + } + throw error; + } }; /** @@ -193,10 +416,51 @@ export class AgentRuntimeClient { config?: Config; }): Promise => { const { agentRuntimeId, input, config } = params; - return AgentRuntime.listVersionsById({ - agentRuntimeId, - input, - config: config ?? this.config, + const cfg = Config.withConfigs(this.config, config); + const versions: AgentRuntimeVersion[] = []; + let page = 1; + const pageSize = 50; + + while (true) { + const request = new $AgentRun.ListAgentRuntimeVersionsRequest({ + ...input, + pageNumber: input?.pageNumber ?? page, + pageSize: input?.pageSize ?? pageSize, + }); + const result = await this.controlApi.listAgentRuntimeVersions({ + agentId: agentRuntimeId, + input: request, + config: cfg, + }); + + if (result.items) { + for (const item of result.items) { + versions.push({ + agentRuntimeArn: item.agentRuntimeArn, + agentRuntimeId: item.agentRuntimeId, + agentRuntimeName: item.agentRuntimeName, + agentRuntimeVersion: item.agentRuntimeVersion, + description: item.description, + lastUpdatedAt: item.lastUpdatedAt, + }); + } + } + + if (!result.items || result.items.length < pageSize) { + break; + } + + page++; + } + + // Deduplicate + const seen = new Set(); + return versions.filter((v) => { + if (!v.agentRuntimeVersion || seen.has(v.agentRuntimeVersion)) { + return false; + } + seen.add(v.agentRuntimeVersion); + return true; }); }; @@ -223,11 +487,11 @@ export class AgentRuntimeClient { params: { agentRuntimeName: string; agentRuntimeEndpointName?: string; - } & InvokeArgs, + } & InvokeArgs ) => { const { agentRuntimeName, - agentRuntimeEndpointName = "Default", + agentRuntimeEndpointName = 'Default', messages, stream, config, @@ -240,7 +504,7 @@ export class AgentRuntimeClient { const dataApi = new AgentRuntimeDataAPI( agentRuntimeName, agentRuntimeEndpointName, - cfg, + cfg ); return dataApi.invokeOpenai({ diff --git a/src/agent-runtime/endpoint.ts b/src/agent-runtime/endpoint.ts index 4a36de2..dfdb884 100644 --- a/src/agent-runtime/endpoint.ts +++ b/src/agent-runtime/endpoint.ts @@ -5,27 +5,31 @@ * This module defines the Agent Runtime Endpoint resource class. */ -import * as $AgentRun from "@alicloud/agentrun20250910"; - -import { Config } from "../utils/config"; -import { HTTPError } from "../utils/exception"; -import { updateObjectProperties } from "../utils/resource"; -import { Status } from "../utils/model"; +import { Config } from '../utils/config'; +import { + listAllResourcesFunction, + ResourceBase, + updateObjectProperties, +} from '../utils/resource'; +import { PageableInput, Status } from '../utils/model'; -import { AgentRuntimeControlAPI } from "./api/control"; -import { AgentRuntimeDataAPI, InvokeArgs } from "./api/data"; +import { AgentRuntimeDataAPI, InvokeArgs } from './api/data'; import { AgentRuntimeEndpointCreateInput, AgentRuntimeEndpointUpdateInput, AgentRuntimeEndpointListInput, AgentRuntimeEndpointData, AgentRuntimeEndpointRoutingConfig, -} from "./model"; +} from './model'; +import { KeyOf } from 'zod/v4/core/util.cjs'; /** * Agent Runtime Endpoint resource class */ -export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { +export class AgentRuntimeEndpoint + extends ResourceBase + implements AgentRuntimeEndpointData +{ // System properties agentRuntimeEndpointArn?: string; agentRuntimeEndpointId?: string; @@ -35,49 +39,29 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { endpointPublicUrl?: string; resourceName?: string; routingConfiguration?: AgentRuntimeEndpointRoutingConfig; - status?: Status; + declare status?: Status; statusReason?: string; tags?: string[]; targetVersion?: string; - private _config?: Config; + protected _config?: Config; private _dataApi?: AgentRuntimeDataAPI; private _agentRuntimeName?: string; - constructor(data?: Partial, config?: Config) { + constructor(data?: any, config?: Config) { + super(); if (data) { updateObjectProperties(this, data); } this._config = config; } - /** - * Create endpoint from SDK response object - */ - static fromInnerObject( - obj: $AgentRun.AgentRuntimeEndpoint, - config?: Config, - ): AgentRuntimeEndpoint { - return new AgentRuntimeEndpoint( - { - agentRuntimeEndpointArn: obj.agentRuntimeEndpointArn, - agentRuntimeEndpointId: obj.agentRuntimeEndpointId, - agentRuntimeEndpointName: obj.agentRuntimeEndpointName, - agentRuntimeId: obj.agentRuntimeId, - description: obj.description, - endpointPublicUrl: obj.endpointPublicUrl, - resourceName: obj.resourceName, - status: obj.status as Status, - statusReason: obj.statusReason, - tags: obj.tags, - targetVersion: obj.targetVersion, - }, - config, - ); - } + uniqIdCallback = () => this.agentRuntimeEndpointId; - private static getClient(): AgentRuntimeControlAPI { - return new AgentRuntimeControlAPI(); + private static getClient() { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { AgentRuntimeClient } = require('./client'); + return new AgentRuntimeClient(); } /** @@ -89,31 +73,11 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { config?: Config; }): Promise { const { agentRuntimeId, input, config } = params; - const client = AgentRuntimeEndpoint.getClient(); - try { - // Set default targetVersion to "LATEST" if not provided (same as Python SDK) - const targetVersion = input.targetVersion || 'LATEST'; - - const result = await client.createAgentRuntimeEndpoint({ - agentId: agentRuntimeId, - input: new $AgentRun.CreateAgentRuntimeEndpointInput({ - agentRuntimeEndpointName: input.agentRuntimeEndpointName, - description: input.description, - tags: input.tags, - targetVersion: targetVersion, - }), - config, - }); - return AgentRuntimeEndpoint.fromInnerObject(result, config); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError( - "AgentRuntimeEndpoint", - `${agentRuntimeId}/${input.agentRuntimeEndpointName}`, - ); - } - throw error; - } + return await AgentRuntimeEndpoint.getClient().createEndpoint({ + agentRuntimeId, + input, + config, + }); } /** @@ -125,23 +89,11 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { config?: Config; }): Promise { const { agentRuntimeId, endpointId, config } = params; - const client = AgentRuntimeEndpoint.getClient(); - try { - const result = await client.deleteAgentRuntimeEndpoint({ - agentId: agentRuntimeId, - endpointId, - config, - }); - return AgentRuntimeEndpoint.fromInnerObject(result, config); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError( - "AgentRuntimeEndpoint", - `${agentRuntimeId}/${endpointId}`, - ); - } - throw error; - } + return await AgentRuntimeEndpoint.getClient().deleteEndpoint({ + agentRuntimeId, + endpointId, + config, + }); } /** @@ -154,29 +106,12 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { config?: Config; }): Promise { const { agentRuntimeId, endpointId, input, config } = params; - const client = AgentRuntimeEndpoint.getClient(); - try { - const result = await client.updateAgentRuntimeEndpoint({ - agentId: agentRuntimeId, - endpointId, - input: new $AgentRun.UpdateAgentRuntimeEndpointInput({ - agentRuntimeEndpointName: input.agentRuntimeEndpointName, - description: input.description, - tags: input.tags, - targetVersion: input.targetVersion, - }), - config, - }); - return AgentRuntimeEndpoint.fromInnerObject(result, config); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError( - "AgentRuntimeEndpoint", - `${agentRuntimeId}/${endpointId}`, - ); - } - throw error; - } + return await AgentRuntimeEndpoint.getClient().updateEndpoint({ + agentRuntimeId, + endpointId, + input, + config, + }); } /** @@ -188,64 +123,61 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { config?: Config; }): Promise { const { agentRuntimeId, endpointId, config } = params; - const client = AgentRuntimeEndpoint.getClient(); - try { - const result = await client.getAgentRuntimeEndpoint({ - agentId: agentRuntimeId, - endpointId, - config, - }); - return AgentRuntimeEndpoint.fromInnerObject(result, config); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError( - "AgentRuntimeEndpoint", - `${agentRuntimeId}/${endpointId}`, - ); - } - throw error; - } + return await AgentRuntimeEndpoint.getClient().getEndpoint({ + agentRuntimeId, + endpointId, + config, + }); } /** * List endpoints by Agent Runtime ID */ - static async listById(params: { + static async list(params: { agentRuntimeId: string; input?: AgentRuntimeEndpointListInput; config?: Config; }): Promise { const { agentRuntimeId, input, config } = params; - const client = AgentRuntimeEndpoint.getClient(); - try { - const request = new $AgentRun.ListAgentRuntimeEndpointsRequest({ - pageNumber: input?.pageNumber, - pageSize: input?.pageSize, - }); - const result = await client.listAgentRuntimeEndpoints({ - agentId: agentRuntimeId, - input: request, - config, - }); - return (result.items || []).map((item) => - AgentRuntimeEndpoint.fromInnerObject(item, config), - ); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError("AgentRuntime", agentRuntimeId); - } - throw error; - } + return await AgentRuntimeEndpoint.getClient().listEndpoints({ + agentRuntimeId, + input, + config, + }); } + static listAll = async ( + params: { + agentRuntimeId: string; + config?: Config; + } & Omit> + ) => { + const { agentRuntimeId, ...restParams } = params; + + return await listAllResourcesFunction( + (params?: { input?: AgentRuntimeEndpointListInput; config?: Config }) => + this.list({ ...params, agentRuntimeId }) + )(restParams); + }; + + get = async (params?: { config?: Config }) => { + return await AgentRuntimeEndpoint.get({ + agentRuntimeId: this.agentRuntimeId!, + endpointId: this.agentRuntimeEndpointId!, + config: params?.config, + }); + }; + /** * Delete this endpoint */ - delete = async (params?: { config?: Config }): Promise => { + delete = async (params?: { + config?: Config; + }): Promise => { const config = params?.config; if (!this.agentRuntimeId || !this.agentRuntimeEndpointId) { throw new Error( - "agentRuntimeId and agentRuntimeEndpointId are required to delete an endpoint", + 'agentRuntimeId and agentRuntimeEndpointId are required to delete an endpoint' ); } @@ -269,7 +201,7 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { const { input, config } = params; if (!this.agentRuntimeId || !this.agentRuntimeEndpointId) { throw new Error( - "agentRuntimeId and agentRuntimeEndpointId are required to update an endpoint", + 'agentRuntimeId and agentRuntimeEndpointId are required to update an endpoint' ); } @@ -287,11 +219,13 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { /** * Refresh this endpoint's data */ - refresh = async (params?: { config?: Config }): Promise => { + refresh = async (params?: { + config?: Config; + }): Promise => { const config = params?.config; if (!this.agentRuntimeId || !this.agentRuntimeEndpointId) { throw new Error( - "agentRuntimeId and agentRuntimeEndpointId are required to refresh an endpoint", + 'agentRuntimeId and agentRuntimeEndpointId are required to refresh an endpoint' ); } @@ -305,48 +239,6 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { return this; }; - /** - * Wait until the endpoint is ready - */ - waitUntilReady = async ( - options?: { - timeoutSeconds?: number; - intervalSeconds?: number; - beforeCheck?: (endpoint: AgentRuntimeEndpoint) => void; - }, - config?: Config, - ): Promise => { - const timeout = (options?.timeoutSeconds ?? 300) * 1000; - const interval = (options?.intervalSeconds ?? 5) * 1000; - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - await this.refresh({ config }); - - if (options?.beforeCheck) { - options.beforeCheck(this); - } - - if (this.status === Status.READY) { - return this; - } - - if ( - this.status === Status.CREATE_FAILED || - this.status === Status.UPDATE_FAILED || - this.status === Status.DELETE_FAILED - ) { - throw new Error(`Endpoint failed: ${this.statusReason}`); - } - - await new Promise((resolve) => setTimeout(resolve, interval)); - } - - throw new Error( - `Timeout waiting for endpoint to be ready after ${options?.timeoutSeconds ?? 300} seconds`, - ); - }; - /** * Invoke agent runtime using OpenAI-compatible API through this endpoint * @@ -379,8 +271,8 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { // Get agent runtime name if not available if (!this._agentRuntimeName && this.agentRuntimeId) { const client = AgentRuntimeEndpoint.getClient(); - const runtime = await client.getAgentRuntime({ - agentId: this.agentRuntimeId, + const runtime = await client.get({ + id: this.agentRuntimeId, config: cfg, }); this._agentRuntimeName = runtime.agentRuntimeName; @@ -388,14 +280,14 @@ export class AgentRuntimeEndpoint implements AgentRuntimeEndpointData { if (!this._agentRuntimeName) { throw new Error( - "Unable to determine agent runtime name for this endpoint", + 'Unable to determine agent runtime name for this endpoint' ); } this._dataApi = new AgentRuntimeDataAPI( this._agentRuntimeName, - this.agentRuntimeEndpointName || "", - cfg, + this.agentRuntimeEndpointName || '', + cfg ); } diff --git a/src/agent-runtime/runtime.ts b/src/agent-runtime/runtime.ts index d8b21fd..4bd37d6 100644 --- a/src/agent-runtime/runtime.ts +++ b/src/agent-runtime/runtime.ts @@ -5,19 +5,18 @@ * This module defines the Agent Runtime resource class. */ -import * as $AgentRun from "@alicloud/agentrun20250910"; - -import { Config } from "../utils/config"; -import { HTTPError } from "../utils/exception"; -import { Status, NetworkMode } from "../utils/model"; -import { updateObjectProperties } from "../utils/resource"; -import type { NetworkConfig } from "../utils/model"; - -import { AgentRuntimeControlAPI } from "./api/control"; -import { AgentRuntimeDataAPI, InvokeArgs } from "./api/data"; -import { AgentRuntimeEndpoint } from "./endpoint"; +import { Config } from '../utils/config'; +import { Status } from '../utils/model'; +import { + listAllResourcesFunction, + ResourceBase, + updateObjectProperties, +} from '../utils/resource'; +import type { NetworkConfig } from '../utils/model'; + +import { AgentRuntimeDataAPI, InvokeArgs } from './api/data'; +import { AgentRuntimeEndpoint } from './endpoint'; import { - AgentRuntimeArtifact, AgentRuntimeCode, AgentRuntimeContainer, AgentRuntimeCreateInput, @@ -31,12 +30,12 @@ import { AgentRuntimeUpdateInput, AgentRuntimeVersion, AgentRuntimeVersionListInput, -} from "./model"; +} from './model'; /** * Agent Runtime resource class */ -export class AgentRuntime implements AgentRuntimeData { +export class AgentRuntime extends ResourceBase implements AgentRuntimeData { // System properties agentRuntimeArn?: string; agentRuntimeId?: string; @@ -61,63 +60,28 @@ export class AgentRuntime implements AgentRuntimeData { resourceName?: string; sessionConcurrencyLimitPerInstance?: number; sessionIdleTimeoutSeconds?: number; - status?: Status; + declare status?: Status; statusReason?: string; tags?: string[]; - private _config?: Config; + protected _config?: Config; private _dataApiCache: Record = {}; - constructor(data?: Partial, config?: Config) { + constructor(data?: any, config?: Config) { + super(); + if (data) { updateObjectProperties(this, data); } this._config = config; } - /** - * Create runtime from SDK response object - */ - static fromInnerObject( - obj: $AgentRun.AgentRuntime, - config?: Config, - ): AgentRuntime { - return new AgentRuntime( - { - agentRuntimeArn: obj.agentRuntimeArn, - agentRuntimeId: obj.agentRuntimeId, - agentRuntimeName: obj.agentRuntimeName, - agentRuntimeVersion: obj.agentRuntimeVersion, - artifactType: obj.artifactType, - codeConfiguration: obj.codeConfiguration as AgentRuntimeCode | undefined, - containerConfiguration: obj.containerConfiguration, - cpu: obj.cpu, - createdAt: obj.createdAt, - credentialName: obj.credentialName, - description: obj.description, - environmentVariables: obj.environmentVariables, - executionRoleArn: obj.executionRoleArn, - healthCheckConfiguration: obj.healthCheckConfiguration, - lastUpdatedAt: obj.lastUpdatedAt, - logConfiguration: obj.logConfiguration as AgentRuntimeLogConfig | undefined, - memory: obj.memory, - networkConfiguration: obj.networkConfiguration as NetworkConfig | undefined, - port: obj.port, - protocolConfiguration: obj.protocolConfiguration as AgentRuntimeProtocolConfig | undefined, - resourceName: obj.resourceName, - sessionConcurrencyLimitPerInstance: - obj.sessionConcurrencyLimitPerInstance, - sessionIdleTimeoutSeconds: obj.sessionIdleTimeoutSeconds, - status: obj.status as Status, - statusReason: obj.statusReason, - tags: obj.tags, - }, - config, - ); - } + uniqIdCallback = () => this.agentRuntimeId; - private static getClient(): AgentRuntimeControlAPI { - return new AgentRuntimeControlAPI(); + private static getClient() { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { AgentRuntimeClient } = require('./client'); + return new AgentRuntimeClient(); } /** @@ -128,76 +92,7 @@ export class AgentRuntime implements AgentRuntimeData { config?: Config; }): Promise { const { input, config } = params; - const client = AgentRuntime.getClient(); - - // Set default network configuration - if (!input.networkConfiguration) { - input.networkConfiguration = {}; - } - - // Auto-detect artifact type - if (!input.artifactType) { - if (input.codeConfiguration) { - input.artifactType = AgentRuntimeArtifact.CODE; - } else if (input.containerConfiguration) { - input.artifactType = AgentRuntimeArtifact.CONTAINER; - } else { - throw new Error( - "Either codeConfiguration or containerConfiguration must be provided", - ); - } - } - - try { - const result = await client.createAgentRuntime({ - input: new $AgentRun.CreateAgentRuntimeInput({ - agentRuntimeName: input.agentRuntimeName, - artifactType: input.artifactType, - codeConfiguration: input.codeConfiguration - ? new $AgentRun.CodeConfiguration({ - checksum: input.codeConfiguration.checksum, - command: input.codeConfiguration.command, - language: input.codeConfiguration.language, - ossBucketName: input.codeConfiguration.ossBucketName, - ossObjectName: input.codeConfiguration.ossObjectName, - zipFile: input.codeConfiguration.zipFile, - }) - : undefined, - containerConfiguration: input.containerConfiguration - ? new $AgentRun.ContainerConfiguration({ - command: input.containerConfiguration.command, - image: input.containerConfiguration.image, - }) - : undefined, - cpu: input.cpu, - credentialName: input.credentialName, - description: input.description, - environmentVariables: input.environmentVariables, - executionRoleArn: input.executionRoleArn, - memory: input.memory, - networkConfiguration: input.networkConfiguration - ? new $AgentRun.NetworkConfiguration({ - networkMode: input.networkConfiguration.networkMode || NetworkMode.PUBLIC, // 默认使用公网模式 - securityGroupId: input.networkConfiguration.securityGroupId, - vpcId: input.networkConfiguration.vpcId, - vswitchIds: input.networkConfiguration.vSwitchIds, - }) - : undefined, - port: input.port, - sessionConcurrencyLimitPerInstance: - input.sessionConcurrencyLimitPerInstance, - sessionIdleTimeoutSeconds: input.sessionIdleTimeoutSeconds, - tags: input.tags, - }), - config, - }); - return AgentRuntime.fromInnerObject(result, config); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError("AgentRuntime", input.agentRuntimeName); - } - throw error; - } + return await AgentRuntime.getClient().create({ input, config }); } /** @@ -208,39 +103,7 @@ export class AgentRuntime implements AgentRuntimeData { config?: Config; }): Promise { const { id, config } = params; - const client = AgentRuntime.getClient(); - - // First delete all endpoints - const endpoints = await AgentRuntimeEndpoint.listById({ - agentRuntimeId: id, - config, - }); - for (const endpoint of endpoints) { - await endpoint.delete({ config }); - } - - // Wait for all endpoints to be deleted - let remaining = await AgentRuntimeEndpoint.listById({ - agentRuntimeId: id, - config, - }); - while (remaining.length > 0) { - await new Promise((resolve) => setTimeout(resolve, 1000)); - remaining = await AgentRuntimeEndpoint.listById({ - agentRuntimeId: id, - config, - }); - } - - try { - const result = await client.deleteAgentRuntime({ agentId: id, config }); - return AgentRuntime.fromInnerObject(result, config); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError("AgentRuntime", id); - } - throw error; - } + return await AgentRuntime.getClient().delete({ id, config }); } /** @@ -252,50 +115,7 @@ export class AgentRuntime implements AgentRuntimeData { config?: Config; }): Promise { const { id, input, config } = params; - const client = AgentRuntime.getClient(); - try { - const result = await client.updateAgentRuntime({ - agentId: id, - input: new $AgentRun.UpdateAgentRuntimeInput({ - agentRuntimeName: input.agentRuntimeName, - artifactType: input.artifactType, - codeConfiguration: input.codeConfiguration - ? new $AgentRun.CodeConfiguration({ - checksum: input.codeConfiguration.checksum, - command: input.codeConfiguration.command, - language: input.codeConfiguration.language, - ossBucketName: input.codeConfiguration.ossBucketName, - ossObjectName: input.codeConfiguration.ossObjectName, - zipFile: input.codeConfiguration.zipFile, - }) - : undefined, - containerConfiguration: input.containerConfiguration - ? new $AgentRun.ContainerConfiguration({ - command: input.containerConfiguration.command, - image: input.containerConfiguration.image, - }) - : undefined, - cpu: input.cpu, - credentialName: input.credentialName, - description: input.description, - environmentVariables: input.environmentVariables, - executionRoleArn: input.executionRoleArn, - memory: input.memory, - port: input.port, - sessionConcurrencyLimitPerInstance: - input.sessionConcurrencyLimitPerInstance, - sessionIdleTimeoutSeconds: input.sessionIdleTimeoutSeconds, - tags: input.tags, - }), - config, - }); - return AgentRuntime.fromInnerObject(result, config); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError("AgentRuntime", id); - } - throw error; - } + return await AgentRuntime.getClient().update({ id, input, config }); } /** @@ -306,83 +126,21 @@ export class AgentRuntime implements AgentRuntimeData { config?: Config; }): Promise { const { id, config } = params; - const client = AgentRuntime.getClient(); - try { - const result = await client.getAgentRuntime({ agentId: id, config }); - return AgentRuntime.fromInnerObject(result, config); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError("AgentRuntime", id); - } - throw error; - } + return await AgentRuntime.getClient().get({ id, config }); } /** * List Agent Runtimes */ - static async list( - input?: AgentRuntimeListInput, - config?: Config, - ): Promise { - const client = AgentRuntime.getClient(); - const request = new $AgentRun.ListAgentRuntimesRequest({ - pageNumber: input?.pageNumber, - pageSize: input?.pageSize, - agentRuntimeName: input?.agentRuntimeName, - tags: input?.tags, - }); - const result = await client.listAgentRuntimes({ input: request, config }); - return (result.items || []).map((item) => - AgentRuntime.fromInnerObject(item, config), - ); + static async list(params?: { + input?: AgentRuntimeListInput; + config?: Config; + }): Promise { + const { input, config } = params ?? {}; + return await AgentRuntime.getClient().list({ input, config }); } - /** - * List all Agent Runtimes (with pagination) - */ - static async listAll( - options?: { - agentRuntimeName?: string; - tags?: string; - searchMode?: string; - }, - config?: Config, - ): Promise { - const runtimes: AgentRuntime[] = []; - let page = 1; - const pageSize = 50; - - while (true) { - const result = await AgentRuntime.list( - { - pageNumber: page, - pageSize, - agentRuntimeName: options?.agentRuntimeName, - tags: options?.tags, - searchMode: options?.searchMode, - }, - config, - ); - - runtimes.push(...result); - page++; - - if (result.length < pageSize) { - break; - } - } - - // Deduplicate - const seen = new Set(); - return runtimes.filter((r) => { - if (!r.agentRuntimeId || seen.has(r.agentRuntimeId)) { - return false; - } - seen.add(r.agentRuntimeId); - return true; - }); - } + static listAll = listAllResourcesFunction(this.list); /** * List Agent Runtime versions by ID @@ -394,50 +152,7 @@ export class AgentRuntime implements AgentRuntimeData { }): Promise { const { agentRuntimeId, input, config } = params; const client = AgentRuntime.getClient(); - const versions: AgentRuntimeVersion[] = []; - let page = 1; - const pageSize = 50; - - while (true) { - const request = new $AgentRun.ListAgentRuntimeVersionsRequest({ - pageNumber: input?.pageNumber ?? page, - pageSize: input?.pageSize ?? pageSize, - }); - const result = await client.listAgentRuntimeVersions({ - agentId: agentRuntimeId, - input: request, - config, - }); - - if (result.items) { - for (const item of result.items) { - versions.push({ - agentRuntimeArn: item.agentRuntimeArn, - agentRuntimeId: item.agentRuntimeId, - agentRuntimeName: item.agentRuntimeName, - agentRuntimeVersion: item.agentRuntimeVersion, - description: item.description, - lastUpdatedAt: item.lastUpdatedAt, - }); - } - } - - if (!result.items || result.items.length < pageSize) { - break; - } - - page++; - } - - // Deduplicate - const seen = new Set(); - return versions.filter((v) => { - if (!v.agentRuntimeVersion || seen.has(v.agentRuntimeVersion)) { - return false; - } - seen.add(v.agentRuntimeVersion); - return true; - }); + return await client.listVersions({ agentRuntimeId, input, config }); } /** @@ -446,7 +161,7 @@ export class AgentRuntime implements AgentRuntimeData { delete = async (params?: { config?: Config }): Promise => { const config = params?.config; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to delete an Agent Runtime"); + throw new Error('agentRuntimeId is required to delete an Agent Runtime'); } const result = await AgentRuntime.delete({ @@ -466,7 +181,7 @@ export class AgentRuntime implements AgentRuntimeData { }): Promise => { const { input, config } = params; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to update an Agent Runtime"); + throw new Error('agentRuntimeId is required to update an Agent Runtime'); } const result = await AgentRuntime.update({ @@ -481,10 +196,10 @@ export class AgentRuntime implements AgentRuntimeData { /** * Refresh this runtime's data */ - refresh = async (params?: { config?: Config }): Promise => { + get = async (params?: { config?: Config }): Promise => { const config = params?.config; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to refresh an Agent Runtime"); + throw new Error('agentRuntimeId is required to refresh an Agent Runtime'); } const result = await AgentRuntime.get({ @@ -504,7 +219,7 @@ export class AgentRuntime implements AgentRuntimeData { }): Promise => { const { input, config } = params; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to create an endpoint"); + throw new Error('agentRuntimeId is required to create an endpoint'); } return AgentRuntimeEndpoint.create({ @@ -523,7 +238,7 @@ export class AgentRuntime implements AgentRuntimeData { }): Promise => { const { endpointId, config } = params; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to delete an endpoint"); + throw new Error('agentRuntimeId is required to delete an endpoint'); } return AgentRuntimeEndpoint.delete({ @@ -543,7 +258,7 @@ export class AgentRuntime implements AgentRuntimeData { }): Promise => { const { endpointId, input, config } = params; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to update an endpoint"); + throw new Error('agentRuntimeId is required to update an endpoint'); } return AgentRuntimeEndpoint.update({ @@ -563,7 +278,7 @@ export class AgentRuntime implements AgentRuntimeData { }): Promise => { const { endpointId, config } = params; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to get an endpoint"); + throw new Error('agentRuntimeId is required to get an endpoint'); } return AgentRuntimeEndpoint.get({ @@ -576,13 +291,15 @@ export class AgentRuntime implements AgentRuntimeData { /** * List endpoints of this runtime */ - listEndpoints = async (params?: { config?: Config }): Promise => { + listEndpoints = async (params?: { + config?: Config; + }): Promise => { const config = params?.config; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to list endpoints"); + throw new Error('agentRuntimeId is required to list endpoints'); } - return AgentRuntimeEndpoint.listById({ + return AgentRuntimeEndpoint.list({ agentRuntimeId: this.agentRuntimeId, config: config ?? this._config, }); @@ -591,10 +308,12 @@ export class AgentRuntime implements AgentRuntimeData { /** * List versions of this runtime */ - listVersions = async (params?: { config?: Config }): Promise => { + listVersions = async (params?: { + config?: Config; + }): Promise => { const config = params?.config; if (!this.agentRuntimeId) { - throw new Error("agentRuntimeId is required to list versions"); + throw new Error('agentRuntimeId is required to list versions'); } return AgentRuntime.listVersionsById({ @@ -603,89 +322,6 @@ export class AgentRuntime implements AgentRuntimeData { }); }; - /** - * Wait until the runtime is ready - */ - waitUntilReady = async ( - options?: { - timeoutSeconds?: number; - intervalSeconds?: number; - beforeCheck?: (runtime: AgentRuntime) => void; - }, - config?: Config, - ): Promise => { - const timeout = (options?.timeoutSeconds ?? 300) * 1000; - const interval = (options?.intervalSeconds ?? 5) * 1000; - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - await this.refresh({ config }); - - if (options?.beforeCheck) { - options.beforeCheck(this); - } - - if (this.status === Status.READY) { - return this; - } - - if ( - this.status === Status.CREATE_FAILED || - this.status === Status.UPDATE_FAILED || - this.status === Status.DELETE_FAILED - ) { - throw new Error(`Agent Runtime failed: ${this.statusReason}`); - } - - await new Promise((resolve) => setTimeout(resolve, interval)); - } - - throw new Error( - `Timeout waiting for Agent Runtime to be ready after ${options?.timeoutSeconds ?? 300} seconds`, - ); - }; - - /** - * Wait until agent runtime reaches READY or any FAILED state - * Similar to waitUntilReady but does not throw on FAILED states - * Compatible with Python SDK's wait_until_ready_or_failed method - */ - waitUntilReadyOrFailed = async ( - options?: { - timeoutSeconds?: number; - intervalSeconds?: number; - beforeCheck?: (runtime: AgentRuntime) => void; - }, - config?: Config, - ): Promise => { - const timeout = (options?.timeoutSeconds ?? 300) * 1000; - const interval = (options?.intervalSeconds ?? 5) * 1000; - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - await this.refresh({ config }); - - if (options?.beforeCheck) { - options.beforeCheck(this); - } - - // Check if reached any final state - if ( - this.status === Status.READY || - this.status === Status.CREATE_FAILED || - this.status === Status.UPDATE_FAILED || - this.status === Status.DELETE_FAILED - ) { - return this; - } - - await new Promise((resolve) => setTimeout(resolve, interval)); - } - - throw new Error( - `Timeout waiting for Agent Runtime to reach final state after ${options?.timeoutSeconds ?? 300} seconds`, - ); - }; /** * Invoke agent runtime using OpenAI-compatible API @@ -707,17 +343,17 @@ export class AgentRuntime implements AgentRuntimeData { * ``` */ invokeOpenai = async ( - args: InvokeArgs & { agentRuntimeEndpointName?: string }, + args: InvokeArgs & { agentRuntimeEndpointName?: string } ) => { const { - agentRuntimeEndpointName = "Default", + agentRuntimeEndpointName = 'Default', messages, stream, config, } = args; if (!this.agentRuntimeName) { - throw new Error("agentRuntimeName is required to invoke OpenAI"); + throw new Error('agentRuntimeName is required to invoke OpenAI'); } // Merge configs @@ -728,7 +364,7 @@ export class AgentRuntime implements AgentRuntimeData { this._dataApiCache[agentRuntimeEndpointName] = new AgentRuntimeDataAPI( this.agentRuntimeName, agentRuntimeEndpointName, - cfg, + cfg ); } diff --git a/src/credential/client.ts b/src/credential/client.ts index 3ab1b88..f444090 100644 --- a/src/credential/client.ts +++ b/src/credential/client.ts @@ -7,9 +7,8 @@ import { HTTPError } from '../utils'; import { Config } from '../utils/config'; -import { fromInnerObject } from '../utils/model'; -import * as agentrun from '@alicloud/agentrun20250910'; +import * as $AgentRun from '@alicloud/agentrun20250910'; import { CredentialControlAPI } from './api/control'; import { Credential } from './credential'; import { @@ -43,16 +42,18 @@ export class CredentialClient { }): Promise => { try { const { input, config } = params; + const cfg = Config.withConfigs(this.config, config); // Normalize credentialConfig to SDK expected field names. - const cfg = input.credentialConfig as any | undefined; + const credCfg = input.credentialConfig as any | undefined; const normalized = { ...input, - credentialAuthType: cfg?.credentialAuthType ?? cfg?.authType, - credentialSourceType: cfg?.credentialSourceType ?? cfg?.sourceType, + credentialAuthType: credCfg?.credentialAuthType ?? credCfg?.authType, + credentialSourceType: + credCfg?.credentialSourceType ?? credCfg?.sourceType, credentialPublicConfig: - cfg?.credentialPublicConfig ?? cfg?.publicConfig, - credentialSecret: cfg?.credentialSecret ?? cfg?.secret, + credCfg?.credentialPublicConfig ?? credCfg?.publicConfig, + credentialSecret: credCfg?.credentialSecret ?? credCfg?.secret, }; // Ensure users field is always present in credentialPublicConfig @@ -80,14 +81,12 @@ export class CredentialClient { normalized.credentialSecret = ''; } } - const result = await this.controlApi.createCredential({ - input: new agentrun.CreateCredentialInput(normalized), - config: config ?? this.config, + input: new $AgentRun.CreateCredentialInput(normalized), + config: cfg, }); - const credential = fromInnerObject(result); - return new Credential(credential); + return new Credential(result); } catch (error) { if (error instanceof HTTPError) { throw error.toResourceError( @@ -103,16 +102,19 @@ export class CredentialClient { /** * Delete a Credential */ - delete = async (params: { name: string; config?: Config }): Promise => { + delete = async (params: { + name: string; + config?: Config; + }): Promise => { try { const { name, config } = params; + const cfg = Config.withConfigs(this.config, config); const result = await this.controlApi.deleteCredential({ credentialName: name, - config: config ?? this.config, + config: cfg, }); - const credential = fromInnerObject(result); - return new Credential(credential); + return new Credential(result); } catch (error) { if (error instanceof HTTPError) { throw error.toResourceError('Credential', params?.name); @@ -132,16 +134,18 @@ export class CredentialClient { }): Promise => { try { const { name, input, config } = params; - const cfg = input.credentialConfig as any | undefined; + const cfg = Config.withConfigs(this.config, config); + const credCfg = input.credentialConfig as any | undefined; const normalized: any = { ...input }; - if (cfg) { + if (credCfg) { normalized.credentialAuthType = - cfg?.credentialAuthType ?? cfg?.authType; + credCfg?.credentialAuthType ?? credCfg?.authType; normalized.credentialSourceType = - cfg?.credentialSourceType ?? cfg?.sourceType; + credCfg?.credentialSourceType ?? credCfg?.sourceType; normalized.credentialPublicConfig = - cfg?.credentialPublicConfig ?? cfg?.publicConfig; - normalized.credentialSecret = cfg?.credentialSecret ?? cfg?.secret; + credCfg?.credentialPublicConfig ?? credCfg?.publicConfig; + normalized.credentialSecret = + credCfg?.credentialSecret ?? credCfg?.secret; // Ensure users field is always present in credentialPublicConfig if (normalized.credentialPublicConfig) { @@ -166,11 +170,10 @@ export class CredentialClient { } } } - const result = await this.controlApi.updateCredential({ credentialName: name, - input: new agentrun.UpdateCredentialInput(normalized), - config: config ?? this.config, + input: new $AgentRun.UpdateCredentialInput(normalized), + config: cfg, }); return new Credential(result as any); @@ -186,13 +189,17 @@ export class CredentialClient { /** * Get a Credential */ - get = async (params: { name: string; config?: Config }): Promise => { + get = async (params: { + name: string; + config?: Config; + }): Promise => { try { const { name, config } = params; + const cfg = Config.withConfigs(this.config, config); const result = await this.controlApi.getCredential({ credentialName: name, - config: config ?? this.config, + config: cfg, }); return new Credential(result as any); } catch (error) { @@ -207,18 +214,22 @@ export class CredentialClient { /** * List Credentials */ - list = async (params: { + list = async (params?: { input?: CredentialListInput; config?: Config; }): Promise => { try { - const { input, config } = params; + const { input, config } = params ?? {}; + const cfg = Config.withConfigs(this.config, config); const results = await this.controlApi.listCredentials({ - input: new agentrun.ListCredentialsRequest(input), - config: config ?? this.config, + input: new $AgentRun.ListCredentialsRequest({ ...input }), + config: cfg, }); - return results.items?.map((item) => new CredentialListOutput(item as any)) ?? []; + return ( + results.items?.map((item) => new CredentialListOutput(item as any)) ?? + [] + ); } catch (error) { if (error instanceof HTTPError) { throw error.toResourceError('Credential'); diff --git a/src/credential/credential.ts b/src/credential/credential.ts index 338cf70..416cc34 100644 --- a/src/credential/credential.ts +++ b/src/credential/credential.ts @@ -6,8 +6,10 @@ */ import { Config } from '../utils/config'; -import { HTTPError } from '../utils/exception'; -import { updateObjectProperties } from '../utils/resource'; +import { + listAllResourcesFunction, + updateObjectProperties, +} from '../utils/resource'; import { ResourceBase } from '../utils/resource'; import { CredentialClient } from './client'; @@ -15,11 +17,9 @@ import { CredentialAuthType, CredentialCreateInput, CredentialInterface, - CredentialListInput, - CredentialListOutput, CredentialSourceType, CredentialUpdateInput, - RelatedResource, + RelatedResource } from './model'; export class Credential extends ResourceBase implements CredentialInterface { @@ -43,7 +43,7 @@ export class Credential extends ResourceBase implements CredentialInterface { protected _config?: Config; - constructor(data?: CredentialInterface, config?: Config) { + constructor(data?: any, config?: Config) { super(); if (data) updateObjectProperties(this, data); @@ -66,15 +66,7 @@ export class Credential extends ResourceBase implements CredentialInterface { const config: Config | undefined = hasInputProp ? paramsOrInput.config : undefined; - const client = Credential.getClient(); - try { - return await client.create({ input, config }); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError('Credential', input.credentialName || ''); - } - throw error; - } + return await Credential.getClient().create({ input, config }); }; /** @@ -87,15 +79,7 @@ export class Credential extends ResourceBase implements CredentialInterface { const config: Config | undefined = isString ? undefined : paramsOrName.config; - const client = Credential.getClient(); - try { - return await client.delete({ name, config }); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError('Credential', name); - } - throw error; - } + return await Credential.getClient().delete({ name, config }); }; /** @@ -106,15 +90,7 @@ export class Credential extends ResourceBase implements CredentialInterface { const name: string = paramsOrName.name; const input: CredentialUpdateInput = paramsOrName.input; const config: Config | undefined = paramsOrName.config; - const client = Credential.getClient(); - try { - return await client.update({ name, input, config }); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError('Credential', name); - } - throw error; - } + return await Credential.getClient().update({ name, input, config }); }; /** @@ -127,15 +103,7 @@ export class Credential extends ResourceBase implements CredentialInterface { const config: Config | undefined = isString ? undefined : paramsOrName.config; - const client = Credential.getClient(); - try { - return await client.get({ name, config }); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError('Credential', name); - } - throw error; - } + return await Credential.getClient().get({ name, config }); }; /** @@ -144,28 +112,10 @@ export class Credential extends ResourceBase implements CredentialInterface { static list = async (paramsOrUndefined?: any) => { const input = paramsOrUndefined?.input ?? paramsOrUndefined; const config: Config | undefined = paramsOrUndefined?.config; - const client = Credential.getClient(); - try { - return await client.list({ input, config }); - } catch (error) { - if (error instanceof HTTPError) { - throw error.toResourceError('Credential', 'list'); - } - throw error; - } + return await Credential.getClient().list({ input, config }); }; - static listAll = async (params?: { - input?: CredentialListInput; - config?: Config; - }) => { - const result = await super.listAll({ - uniqIdCallback: (item) => item.credentialName || '', - input: params?.input, - config: params?.config, - }); - return result as CredentialListOutput[]; - }; + static listAll = listAllResourcesFunction(this.list); /** * Delete this credential @@ -202,7 +152,7 @@ export class Credential extends ResourceBase implements CredentialInterface { input, config: config ?? this._config, }); - + updateObjectProperties(this, result); return this; }; diff --git a/src/credential/model.ts b/src/credential/model.ts index dd94d39..3d86396 100644 --- a/src/credential/model.ts +++ b/src/credential/model.ts @@ -18,7 +18,10 @@ export type CredentialAuthType = | 'custom_header'; /** 凭证来源类型 / Credential Source Types */ -export type CredentialSourceType = 'external_llm' | 'external_tool' | 'internal'; +export type CredentialSourceType = + | 'external_llm' + | 'external_tool' + | 'internal'; /** * Credential basic authentication configuration @@ -139,7 +142,9 @@ export class CredentialConfig implements CredentialConfigInterface { } /** 配置访问第三方工具的自定义凭证 */ - static outboundToolAKSKCustom(params: { authConfig: Record }) { + static outboundToolAKSKCustom(params: { + authConfig: Record; + }) { const { authConfig } = params; return new CredentialConfig({ credentialSourceType: 'external_tool', @@ -217,6 +222,8 @@ export class CredentialListOutput { if (data) updateObjectProperties(this, data); } + uniqIdCallback = () => this.credentialId; + toCredential = async (params?: { config?: Config }) => { const { CredentialClient } = await import('./client'); return await new CredentialClient(params?.config).get({ diff --git a/src/model/api/model-api.ts b/src/model/api/model-api.ts new file mode 100644 index 0000000..8f768a8 --- /dev/null +++ b/src/model/api/model-api.ts @@ -0,0 +1,83 @@ +import { Config } from '@/utils'; +import { createOpenAICompatible } from '@ai-sdk/openai-compatible'; + +export interface ModelInfo { + model?: string; + apiKey?: string; + baseUrl?: string; + headers?: Record; + provider?: string; +} + +export type GetModelInfo = (params?: { config?: Config }) => Promise; + +export class ModelAPI { + getModelInfo: GetModelInfo; + constructor(getModelInfo: GetModelInfo) { + this.getModelInfo = getModelInfo; + } + // abstract modelInfo(params: { config?: Config }): Promise; + + private getProvider = async (params: { model?: string; config?: Config }) => { + const { model, config } = params; + + const info = await this.getModelInfo({ config }); + const provider = createOpenAICompatible({ + name: model || info.model || '', + apiKey: info.apiKey, + baseURL: 'http://127.0.0.1:8080', // info.baseUrl, + headers: info.headers, + }); + + return { provider, model: model || info.model || '' }; + }; + + private getModel = async (params: Parameters[0]) => { + const { provider, model } = await this.getProvider(params); + return provider(model); + }; + + private getEmbeddingModel = async ( + params: Parameters[0] + ) => { + const { provider, model } = await this.getProvider(params); + return provider.embeddingModel(model); + }; + + completion = async (params: { + messages: any[]; + model?: string; + stream?: boolean; + config?: Config; + [key: string]: any; + }): Promise< + | import('ai').StreamTextResult + | import('ai').GenerateTextResult + > => { + const { messages, model, stream = false, config, ...kwargs } = params; + const { streamText, generateText } = await import('ai'); + + return await (stream ? streamText : generateText)({ + model: await this.getModel({ model, config }), + messages, + ...kwargs, + }); + }; + + embedding = async (params: { + values: string[]; + model?: string; + stream?: boolean; + config?: Config; + [key: string]: any; + }) => { + const { values, model, config, ...kwargs } = params; + const { embedMany } = await import('ai'); + + return await embedMany({ + model: await this.getEmbeddingModel({ model, config }), + values, + ...kwargs, + }); + }; +} diff --git a/src/model/client.ts b/src/model/client.ts index 6b73c19..4a234a5 100644 --- a/src/model/client.ts +++ b/src/model/client.ts @@ -23,8 +23,8 @@ import { ModelService } from './model-service'; * Provides create, delete, update and query functions for model services and model proxies. */ export class ModelClient { - private controlApi: ModelControlAPI; private config?: Config; + private controlApi: ModelControlAPI; /** * 初始化客户端 / Initialize client @@ -45,7 +45,7 @@ export class ModelClient { */ create = async (params: { input: ModelServiceCreateInput | ModelProxyCreateInput; config?: Config }): Promise => { const { input, config } = params; - const cfg = config ?? this.config; + const cfg = Config.withConfigs(this.config, config); try { if ('modelProxyName' in input) { @@ -59,17 +59,10 @@ export class ModelClient { } const createInput = new $AgentRun.CreateModelProxyInput({ - modelProxyName: modelProxyInput.modelProxyName, - description: modelProxyInput.description, - executionRoleArn: modelProxyInput.executionRoleArn, - tags: modelProxyInput.tags, - cpu: modelProxyInput.cpu ?? 2, // 默认值 2 - litellmVersion: modelProxyInput.litellmVersion, - memory: modelProxyInput.memory ?? 4096, // 默认值 4096 + ...modelProxyInput, + cpu: modelProxyInput.cpu ?? 2, // 默认值 2 + memory: modelProxyInput.memory ?? 4096, // 默认值 4096 proxyMode: modelProxyInput.proxyModel, - serviceRegionId: modelProxyInput.serviceRegionId, - proxyConfig: modelProxyInput.proxyConfig, - modelType: modelProxyInput.modelType, }); const result = await this.controlApi.createModelProxy({ @@ -84,13 +77,7 @@ export class ModelClient { const modelServiceInput = input as ModelServiceCreateInput; const createInput = new $AgentRun.CreateModelServiceInput({ - modelServiceName: modelServiceInput.modelServiceName, - description: modelServiceInput.description, - tags: modelServiceInput.tags, - provider: modelServiceInput.provider, - modelInfoConfigs: modelServiceInput.modelInfoConfigs, - providerSettings: modelServiceInput.providerSettings, - modelType: modelServiceInput.modelType, + ...modelServiceInput, }); const result = await this.controlApi.createModelService({ @@ -123,7 +110,7 @@ export class ModelClient { */ delete = async (params: { name: string; backendType?: BackendType; config?: Config }): Promise => { const { name, backendType, config } = params; - const cfg = config ?? this.config; + const cfg = Config.withConfigs(this.config, config); let error: HTTPError | null = null; // 如果是 proxy 或未指定类型,先尝试删除 proxy @@ -178,7 +165,7 @@ export class ModelClient { */ update = async (params: { name: string; input: ModelServiceUpdateInput | ModelProxyUpdateInput; config?: Config }): Promise => { const { name, input, config } = params; - const cfg = config ?? this.config; + const cfg = Config.withConfigs(this.config, config); if ('proxyModel' in input || 'executionRoleArn' in input) { // 处理 ModelProxyUpdateInput @@ -192,8 +179,8 @@ export class ModelClient { } const updateInput = new $AgentRun.UpdateModelProxyInput({ - description: modelProxyInput.description, - executionRoleArn: modelProxyInput.executionRoleArn, + ...modelProxyInput, + proxyMode: modelProxyInput.proxyModel, }); const result = await this.controlApi.updateModelProxy({ @@ -216,8 +203,7 @@ export class ModelClient { try { const updateInput = new $AgentRun.UpdateModelServiceInput({ - description: modelServiceInput.description, - providerSettings: modelServiceInput.providerSettings, + ...modelServiceInput, }); const result = await this.controlApi.updateModelService({ @@ -248,7 +234,7 @@ export class ModelClient { */ get = async (params: { name: string; backendType?: BackendType; config?: Config }): Promise => { const { name, backendType, config } = params; - const cfg = config ?? this.config; + const cfg = Config.withConfigs(this.config, config); let error: HTTPError | null = null; // 如果是 proxy 或未指定类型,先尝试获取 proxy @@ -299,18 +285,16 @@ export class ModelClient { * @param params - 参数 / Parameters * @returns 模型服务列表 / Model service list */ - list = async (params: { input: ModelServiceListInput | ModelProxyListInput; config?: Config }): Promise => { - const { input, config } = params; - const cfg = config ?? this.config; + list = async (params?: { input?: ModelServiceListInput | ModelProxyListInput; config?: Config }): Promise => { + const { input, config } = params ?? {}; + const cfg = Config.withConfigs(this.config, config); - if ('modelProxyName' in input) { + if (input && 'modelProxyName' in input) { // 处理 ModelProxyListInput const modelProxyInput = input as ModelProxyListInput; const request = new $AgentRun.ListModelProxiesRequest({ - pageNumber: modelProxyInput.pageNumber, - pageSize: modelProxyInput.pageSize, - modelProxyName: modelProxyInput.modelProxyName, + ...modelProxyInput, }); const result = await this.controlApi.listModelProxies({ @@ -323,15 +307,11 @@ export class ModelClient { return proxy; }); } else { - // 处理 ModelServiceListInput - const modelServiceInput = input as ModelServiceListInput; + // 处理 ModelServiceListInput 或无参数(默认列出 ModelService) + const modelServiceInput = (input ?? {}) as ModelServiceListInput; const request = new $AgentRun.ListModelServicesRequest({ - pageNumber: modelServiceInput.pageNumber, - pageSize: modelServiceInput.pageSize, - modelServiceName: modelServiceInput.modelServiceName, - modelType: modelServiceInput.modelType, - provider: modelServiceInput.provider, + ...modelServiceInput, }); const result = await this.controlApi.listModelServices({ diff --git a/src/model/model-proxy.ts b/src/model/model-proxy.ts index de35516..1d40df8 100644 --- a/src/model/model-proxy.ts +++ b/src/model/model-proxy.ts @@ -8,10 +8,9 @@ import * as _ from 'lodash'; import { Config } from '../utils/config'; -import { Status } from '../utils/model'; -import { PageableInput } from '../utils/model'; -import { ResourceBase } from '../utils/resource'; +import { listAllResourcesFunction, ResourceBase } from '../utils/resource'; +import { ModelAPI, ModelInfo } from './api/model-api'; import { BackendType, ModelProxyCreateInput, @@ -58,9 +57,21 @@ export class ModelProxy lastUpdatedAt?: string; declare status?: ModelProxySystemProps['status']; + private modelApi: ModelAPI; + + constructor() { + super(); + this.modelApi = new ModelAPI(this.modelInfo); + this.completion = this.modelApi.completion; + this.embedding = this.modelApi.embedding; + } + + completion: (typeof ModelAPI)['prototype']['completion']; + embedding: (typeof ModelAPI)['prototype']['embedding']; + /** * 获取客户端 / Get client - * + * * @returns ModelClient 实例 */ private static getClient() { @@ -70,9 +81,11 @@ export class ModelProxy return new ModelClient(); } + uniqIdCallback = () => this.modelProxyId; + /** * 创建模型代理 / Create model proxy - * + * * @param params - 参数 / Parameters * @returns 创建的模型代理对象 / Created model proxy object */ @@ -86,7 +99,7 @@ export class ModelProxy /** * 根据名称删除模型代理 / Delete model proxy by name - * + * * @param params - 参数 / Parameters * @returns 删除的模型代理对象 / Deleted model proxy object */ @@ -98,13 +111,13 @@ export class ModelProxy return await this.getClient().delete({ name, backendType: BackendType.PROXY, - config + config, }); } /** * 根据名称更新模型代理 / Update model proxy by name - * + * * @param params - 参数 / Parameters * @returns 更新后的模型代理对象 / Updated model proxy object */ @@ -119,7 +132,7 @@ export class ModelProxy /** * 根据名称获取模型代理 / Get model proxy by name - * + * * @param params - 参数 / Parameters * @returns 模型代理对象 / Model proxy object */ @@ -131,78 +144,38 @@ export class ModelProxy return await this.getClient().get({ name, backendType: BackendType.PROXY, - config + config, }); } /** * 列出模型代理(分页)/ List model proxies (paginated) - * + * * @param pageInput - 分页参数 / Pagination parameters * @param config - 配置 / Configuration * @param kwargs - 其他查询参数 / Other query parameters * @returns 模型代理列表 / Model proxy list */ - protected static async listPage( - pageInput: PageableInput, - config?: Config, - kwargs?: Partial - ): Promise { + static list = async (params?: { + input?: ModelProxyListInput; + config?: Config; + }): Promise => { + const { input, config } = params ?? {}; + return await this.getClient().list({ input: { modelProxyName: undefined, // 标识这是 ModelProxyListInput - ...kwargs, - ...pageInput, + ...input, } as ModelProxyListInput, - config + config, }); - } - - /** - * 列出所有模型代理 / List all model proxies - * - * @param options - 查询选项 / Query options - * @param config - 配置 / Configuration - * @returns 模型代理列表 / Model proxy list - */ - static async listAll(options?: { - proxyMode?: string; - status?: Status; - config?: Config; - }): Promise { - const allResults: ModelProxy[] = []; - let page = 1; - const pageSize = 50; - while (true) { - const pageResults = await this.listPage( - { pageNumber: page, pageSize }, - options?.config, - { - proxyMode: options?.proxyMode, - status: options?.status, - } - ); - page += 1; - allResults.push(...pageResults); - if (pageResults.length < pageSize) break; - } + }; - // 去重 - const resultSet = new Set(); - const results: ModelProxy[] = []; - for (const item of allResults) { - const uniqId = item.modelProxyId || ''; - if (!resultSet.has(uniqId)) { - resultSet.add(uniqId); - results.push(item); - } - } - return results; - } + static listAll = listAllResourcesFunction(this.list); /** * 更新模型代理 / Update model proxy - * + * * @param input - 模型代理更新输入参数 / Model proxy update input parameters * @param config - 配置 / Configuration * @returns 更新后的模型代理对象 / Updated model proxy object @@ -213,9 +186,7 @@ export class ModelProxy }): Promise => { const { input, config } = params; if (!this.modelProxyName) { - throw new Error( - 'modelProxyName is required to update a ModelProxy' - ); + throw new Error('modelProxyName is required to update a ModelProxy'); } const result = await ModelProxy.update({ @@ -230,31 +201,30 @@ export class ModelProxy /** * 删除模型代理 / Delete model proxy - * + * * @param params - 参数 / Parameters * @returns 删除的模型代理对象 / Deleted model proxy object */ delete = async (params?: { config?: Config }): Promise => { if (!this.modelProxyName) { - throw new Error( - 'modelProxyName is required to delete a ModelProxy' - ); + throw new Error('modelProxyName is required to delete a ModelProxy'); } - return await ModelProxy.delete({ name: this.modelProxyName, config: params?.config }); + return await ModelProxy.delete({ + name: this.modelProxyName, + config: params?.config, + }); }; /** * 刷新模型代理信息 / Refresh model proxy information - * + * * @param params - 参数 / Parameters * @returns 刷新后的模型代理对象 / Refreshed model proxy object */ get = async (params?: { config?: Config }): Promise => { if (!this.modelProxyName) { - throw new Error( - 'modelProxyName is required to refresh a ModelProxy' - ); + throw new Error('modelProxyName is required to refresh a ModelProxy'); } const result = await ModelProxy.get({ @@ -268,17 +238,12 @@ export class ModelProxy /** * 获取模型信息 / Get model information - * + * * @param params - 参数 / Parameters * @param params.config - 配置 / Configuration * @returns 模型基本信息 / Model base information */ - modelInfo = async (params?: { config?: Config }): Promise<{ - apiKey: string; - baseUrl: string; - model?: string; - headers?: Record; - }> => { + modelInfo = async (params?: { config?: Config }): Promise => { const cfg = Config.withConfigs(this._config, params?.config); if (!this.modelProxyName) { @@ -289,7 +254,7 @@ export class ModelProxy } let apiKey = ''; - + // 如果有 credentialName,从 Credential 获取 if (this.credentialName) { // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -314,139 +279,4 @@ export class ModelProxy headers: cfg.headers, }; }; - - /** - * 调用模型完成 / Call model completion - * - * @param params - 参数 / Parameters - * @returns 完成结果 / Completion result - */ - completions = async (params: { - messages: any[]; - model?: string; - stream?: boolean; - config?: Config; - [key: string]: any; - }): Promise< - | import('ai').StreamTextResult - | import('ai').GenerateTextResult - > => { - const { messages, model, stream = false, config, ...kwargs } = params; - const info = await this.modelInfo({ config }); - - // 使用 AI SDK 实现 - const { generateText, streamText } = await import('ai'); - const { createOpenAI } = await import('@ai-sdk/openai'); - - const provider = createOpenAI({ - apiKey: info.apiKey, - baseURL: info.baseUrl, - headers: info.headers, - }); - - const selectedModel = model || info.model || this.modelProxyName || ''; - - if (stream) { - return await streamText({ - model: provider(selectedModel), - messages, - ...kwargs, - }); - } else { - return await generateText({ - model: provider(selectedModel), - messages, - ...kwargs, - }); - } - }; - - /** - * 获取响应 / Get responses - * - * @param params - 参数 / Parameters - * @returns 响应结果 / Response result - */ - responses = async (params: { - messages: any[]; - model?: string; - stream?: boolean; - config?: Config; - [key: string]: any; - }): Promise => { - const { messages, model, stream = false, config, ...kwargs } = params; - const info = await this.modelInfo({ config }); - - // 使用 AI SDK 实现 - const { generateText, streamText } = await import('ai'); - const { createOpenAI } = await import('@ai-sdk/openai'); - - const provider = createOpenAI({ - apiKey: info.apiKey, - baseURL: info.baseUrl, - headers: info.headers, - }); - - const selectedModel = model || info.model || this.modelProxyName || ''; - - if (stream) { - return await streamText({ - model: provider(selectedModel), - messages, - ...kwargs, - }); - } else { - return await generateText({ - model: provider(selectedModel), - messages, - ...kwargs, - }); - } - }; - - /** - * 等待模型代理就绪 / Wait until model proxy is ready - * - * @param options - 选项 / Options - * @param config - 配置 / Configuration - * @returns 模型代理对象 / Model proxy object - */ - waitUntilReady = async ( - options?: { - timeoutSeconds?: number; - intervalSeconds?: number; - beforeCheck?: (proxy: ModelProxy) => void; - }, - config?: Config - ): Promise => { - const timeout = (options?.timeoutSeconds ?? 300) * 1000; - const interval = (options?.intervalSeconds ?? 5) * 1000; - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - await this.refresh({ config }); - - if (options?.beforeCheck) { - options.beforeCheck(this); - } - - if (this.status === Status.READY) { - return this; - } - - if ( - this.status === Status.CREATE_FAILED || - this.status === Status.UPDATE_FAILED || - this.status === Status.DELETE_FAILED - ) { - throw new Error(`Model proxy failed with status: ${this.status}`); - } - - await new Promise(resolve => setTimeout(resolve, interval)); - } - - throw new Error( - `Timeout waiting for model proxy to be ready after ${options?.timeoutSeconds ?? 300} seconds` - ); - }; } diff --git a/src/model/model-service.ts b/src/model/model-service.ts index 6dcf62b..e0d627d 100644 --- a/src/model/model-service.ts +++ b/src/model/model-service.ts @@ -6,8 +6,8 @@ */ import { Config } from '../utils/config'; -import { Status, PageableInput } from '../utils/model'; -import { ResourceBase } from '../utils/resource'; +import { listAllResourcesFunction, ResourceBase } from '../utils/resource'; +import { ModelAPI } from './api/model-api'; import { BackendType, @@ -16,8 +16,7 @@ import { ModelServiceListInput, ModelServiceMutableProps, ModelServiceSystemProps, - ModelServiceUpdateInput, - ModelType, + ModelServiceUpdateInput } from './model'; /** @@ -34,26 +33,37 @@ export class ModelService modelInfoConfigs?: ModelServiceImmutableProps['modelInfoConfigs']; modelServiceName?: string; provider?: string; - + // MutableProps credentialName?: string; description?: string; networkConfiguration?: ModelServiceMutableProps['networkConfiguration']; tags?: string[]; providerSettings?: ModelServiceMutableProps['providerSettings']; - + // SystemProps modelServiceId?: string; createdAt?: string; lastUpdatedAt?: string; declare status?: ModelServiceSystemProps['status']; - + // CommonProps modelType?: ModelServiceImmutableProps['modelType']; + private modelApi: ModelAPI; + constructor() { + super(); + this.modelApi = new ModelAPI(this.modelInfo); + this.completion = this.modelApi.completion; + this.embedding = this.modelApi.embedding; + } + + completion: (typeof ModelAPI)['prototype']['completion']; + embedding: (typeof ModelAPI)['prototype']['embedding']; + /** * 获取客户端 / Get client - * + * * @returns ModelClient 实例 */ private static getClient() { @@ -63,9 +73,11 @@ export class ModelService return new ModelClient(); } + uniqIdCallback = () => this.modelServiceId; + /** * 创建模型服务 / Create model service - * + * * @param params - 参数 / Parameters * @returns 创建的模型服务对象 / Created model service object */ @@ -79,7 +91,7 @@ export class ModelService /** * 根据名称删除模型服务 / Delete model service by name - * + * * @param params - 参数 / Parameters * @returns 删除的模型服务对象 / Deleted model service object */ @@ -91,13 +103,13 @@ export class ModelService return await this.getClient().delete({ name, backendType: BackendType.SERVICE, - config + config, }); } /** * 根据名称更新模型服务 / Update model service by name - * + * * @param params - 参数 / Parameters * @returns 更新后的模型服务对象 / Updated model service object */ @@ -112,7 +124,7 @@ export class ModelService /** * 根据名称获取模型服务 / Get model service by name - * + * * @param params - 参数 / Parameters * @returns 模型服务对象 / Model service object */ @@ -124,77 +136,37 @@ export class ModelService return await this.getClient().get({ name, backendType: BackendType.SERVICE, - config + config, }); } /** * 列出模型服务(分页)/ List model services (paginated) - * + * * @param pageInput - 分页参数 / Pagination parameters * @param config - 配置 / Configuration * @param kwargs - 其他查询参数 / Other query parameters * @returns 模型服务列表 / Model service list */ - protected static async listPage( - pageInput: PageableInput, - config?: Config, - kwargs?: Partial - ): Promise { + static list = async (params?: { + input?: ModelServiceListInput; + config?: Config; + }): Promise => { + const { input, config } = params ?? {}; + return await this.getClient().list({ input: { - ...kwargs, - ...pageInput, + ...input, } as ModelServiceListInput, - config + config, }); - } - - /** - * 列出所有模型服务 / List all model services - * - * @param options - 查询选项 / Query options - * @param config - 配置 / Configuration - * @returns 模型服务列表 / Model service list - */ - static async listAll(options?: { - modelType?: ModelType; - provider?: string; - config?: Config; - }): Promise { - const allResults: ModelService[] = []; - let page = 1; - const pageSize = 50; - while (true) { - const pageResults = await this.listPage( - { pageNumber: page, pageSize }, - options?.config, - { - modelType: options?.modelType, - provider: options?.provider, - } - ); - page += 1; - allResults.push(...pageResults); - if (pageResults.length < pageSize) break; - } + }; - // 去重 - const resultSet = new Set(); - const results: ModelService[] = []; - for (const item of allResults) { - const uniqId = item.modelServiceId || ''; - if (!resultSet.has(uniqId)) { - resultSet.add(uniqId); - results.push(item); - } - } - return results; - } + static listAll = listAllResourcesFunction(this.list); /** * 更新模型服务 / Update model service - * + * * @param params - 参数 / Parameters * @returns 更新后的模型服务对象 / Updated model service object */ @@ -204,15 +176,13 @@ export class ModelService }): Promise => { const { input, config } = params; if (!this.modelServiceName) { - throw new Error( - 'modelServiceName is required to update a ModelService' - ); + throw new Error('modelServiceName is required to update a ModelService'); } const result = await ModelService.update({ name: this.modelServiceName, input, - config + config, }); this.updateSelf(result); @@ -221,36 +191,35 @@ export class ModelService /** * 删除模型服务 / Delete model service - * + * * @param config - 配置 / Configuration * @returns 删除的模型服务对象 / Deleted model service object */ delete = async (params?: { config?: Config }): Promise => { if (!this.modelServiceName) { - throw new Error( - 'modelServiceName is required to delete a ModelService' - ); + throw new Error('modelServiceName is required to delete a ModelService'); } - return await ModelService.delete({ name: this.modelServiceName, config: params?.config }); + return await ModelService.delete({ + name: this.modelServiceName, + config: params?.config, + }); }; /** * 刷新模型服务信息 / Refresh model service information - * + * * @param config - 配置 / Configuration * @returns 刷新后的模型服务对象 / Refreshed model service object */ get = async (params?: { config?: Config }): Promise => { if (!this.modelServiceName) { - throw new Error( - 'modelServiceName is required to refresh a ModelService' - ); + throw new Error('modelServiceName is required to refresh a ModelService'); } const result = await ModelService.get({ name: this.modelServiceName, - config: params?.config + config: params?.config, }); this.updateSelf(result); @@ -259,12 +228,14 @@ export class ModelService /** * 获取模型信息 / Get model information - * + * * @param params - 参数 / Parameters * @param params.config - 配置 / Configuration * @returns 模型基本信息 / Model base information */ - modelInfo = async (params?: { config?: Config }): Promise<{ + modelInfo = async (params?: { + config?: Config; + }): Promise<{ apiKey: string; baseUrl: string; model?: string; @@ -281,7 +252,7 @@ export class ModelService } let apiKey = this.providerSettings.apiKey || ''; - + // 如果没有 apiKey 但有 credentialName,从 Credential 获取 if (!apiKey && this.credentialName) { // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -307,150 +278,4 @@ export class ModelService provider: this.provider, }; }; - - /** - * 调用模型完成 / Call model completion - * - * @param messages - 消息列表 / Message list - * @param model - 模型名称(可选)/ Model name (optional) - * @param stream - 是否流式输出 / Whether to stream output - * @param kwargs - 其他参数 / Other parameters - * @returns 完成结果 / Completion result - */ - completions = async (params: { - messages: any[]; - model?: string; - stream?: boolean; - config?: Config; - [key: string]: any; - }): Promise< - | import('ai').StreamTextResult - | import('ai').GenerateTextResult - > => { - const { messages, model, stream = false, config, ...kwargs } = params; - const info = await this.modelInfo({ config }); - - // 使用 AI SDK 实现 - const { generateText, streamText } = await import('ai'); - const { createOpenAI } = await import('@ai-sdk/openai'); - - const provider = createOpenAI({ - apiKey: info.apiKey, - baseURL: info.baseUrl, - headers: info.headers, - }); - - const selectedModel = model || info.model || this.modelServiceName || ''; - - if (stream) { - return await streamText({ - model: provider(selectedModel), - messages, - ...kwargs, - }); - } else { - return await generateText({ - model: provider(selectedModel), - messages, - ...kwargs, - }); - } - }; - - /** - * 获取响应 / Get responses - * - * @param input - 输入 / Input - * @param model - 模型名称(可选)/ Model name (optional) - * @param stream - 是否流式输出 / Whether to stream output - * @param kwargs - 其他参数 / Other parameters - * @returns 响应结果 / Response result - */ - responses = async (params: { - input: string | any; - model?: string; - stream?: boolean; - config?: Config; - [key: string]: any; - }): Promise => { - const { input, model, stream = false, config, ...kwargs } = params; - const info = await this.modelInfo({ config }); - - // 使用 AI SDK 实现 - const { generateText, streamText } = await import('ai'); - const { createOpenAI } = await import('@ai-sdk/openai'); - - const provider = createOpenAI({ - apiKey: info.apiKey, - baseURL: info.baseUrl, - headers: info.headers, - }); - - const selectedModel = model || info.model || this.modelServiceName || ''; - - // 将 input 转换为 messages 格式 - const messages = typeof input === 'string' - ? [{ role: 'user' as const, content: input }] - : input; - - if (stream) { - return await streamText({ - model: provider(selectedModel), - messages, - ...kwargs, - }); - } else { - return await generateText({ - model: provider(selectedModel), - messages, - ...kwargs, - }); - } - }; - - /** - * 等待模型服务就绪 / Wait until model service is ready - * - * @param options - 选项 / Options - * @param config - 配置 / Configuration - * @returns 模型服务对象 / Model service object - */ - waitUntilReady = async ( - options?: { - timeoutSeconds?: number; - intervalSeconds?: number; - beforeCheck?: (service: ModelService) => void; - }, - config?: Config - ): Promise => { - const timeout = (options?.timeoutSeconds ?? 300) * 1000; - const interval = (options?.intervalSeconds ?? 5) * 1000; - const startTime = Date.now(); - - while (Date.now() - startTime < timeout) { - await this.refresh({ config }); - - if (options?.beforeCheck) { - options.beforeCheck(this); - } - - if (this.status === Status.READY) { - return this; - } - - if ( - this.status === Status.CREATE_FAILED || - this.status === Status.UPDATE_FAILED || - this.status === Status.DELETE_FAILED - ) { - throw new Error(`Model service failed with status: ${this.status}`); - } - - await new Promise(resolve => setTimeout(resolve, interval)); - } - - throw new Error( - `Timeout waiting for model service to be ready after ${options?.timeoutSeconds ?? 300} seconds` - ); - }; } diff --git a/src/sandbox/aio-sandbox.ts b/src/sandbox/aio-sandbox.ts index 3a43396..17b7153 100644 --- a/src/sandbox/aio-sandbox.ts +++ b/src/sandbox/aio-sandbox.ts @@ -5,19 +5,19 @@ * into a single unified interface. */ -import { Config } from "../utils/config"; -import { logger } from "../utils/log"; -import { ServerError } from "../utils/exception"; +import { Config } from '../utils/config'; +import { logger } from '../utils/log'; +import { ServerError } from '../utils/exception'; -import { AioDataAPI } from "./api/aio-data"; +import { AioDataAPI } from './api/aio-data'; import { CodeLanguage, NASConfig, OSSMountConfig, PolarFsConfig, TemplateType, -} from "./model"; -import { Sandbox } from "./sandbox"; +} from './model'; +import { Sandbox } from './sandbox'; /** * File upload/download operations @@ -70,7 +70,10 @@ export class AioFileSystemOperations { /** * Move a file or directory */ - move = async (params: { source: string; destination: string }): Promise => { + move = async (params: { + source: string; + destination: string; + }): Promise => { return this.sandbox.dataApi.moveFile(params); }; @@ -198,7 +201,7 @@ export class AioContextOperations { cwd?: string; }): Promise => { const language = params?.language || CodeLanguage.PYTHON; - const cwd = params?.cwd || "/home/user"; + const cwd = params?.cwd || '/home/user'; const result = await this.sandbox.dataApi.createContext({ language, @@ -212,17 +215,19 @@ export class AioContextOperations { return this; } - throw new ServerError(500, "Failed to create context"); + throw new ServerError(500, 'Failed to create context'); }; /** * Get a specific context by ID */ - get = async (params?: { contextId?: string }): Promise => { + get = async (params?: { + contextId?: string; + }): Promise => { const id = params?.contextId || this._contextId; if (!id) { - logger.error("context id is not set"); - throw new Error("context id is not set"); + logger.error('context id is not set'); + throw new Error('context id is not set'); } const result = await this.sandbox.dataApi.getContext({ contextId: id }); @@ -234,7 +239,7 @@ export class AioContextOperations { return this; } - throw new ServerError(500, "Failed to get context"); + throw new ServerError(500, 'Failed to get context'); }; /** @@ -254,7 +259,7 @@ export class AioContextOperations { } if (!contextId && !language) { - logger.debug("context id is not set, use default language: python"); + logger.debug('context id is not set, use default language: python'); language = CodeLanguage.PYTHON; } @@ -273,7 +278,7 @@ export class AioContextOperations { const id = params?.contextId || this._contextId; if (!id) { throw new Error( - "context_id is required. Either pass it as parameter or create a context first.", + 'context_id is required. Either pass it as parameter or create a context first.' ); } @@ -304,7 +309,7 @@ export class AioSandbox extends Sandbox { ossMountConfig?: OSSMountConfig; polarFsConfig?: PolarFsConfig; }, - config?: Config, + config?: Config ): Promise { const sandbox = await Sandbox.create( { @@ -314,7 +319,7 @@ export class AioSandbox extends Sandbox { ossMountConfig: options?.ossMountConfig, polarFsConfig: options?.polarFsConfig, }, - config, + config ); const aioSandbox = new AioSandbox(sandbox, config); @@ -337,7 +342,7 @@ export class AioSandbox extends Sandbox { get dataApi(): AioDataAPI { if (!this._dataApi) { this._dataApi = new AioDataAPI({ - sandboxId: this.sandboxId || "", + sandboxId: this.sandboxId || '', config: this._config, }); } @@ -387,56 +392,15 @@ export class AioSandbox extends Sandbox { /** * Check sandbox health */ - checkHealth = async (params?: { config?: Config }): Promise<{ status: string; [key: string]: any }> => { - return this.dataApi.checkHealth({ - sandboxId: this.sandboxId!, - config: params?.config + checkHealth = async (params?: { + config?: Config; + }): Promise<{ status: string; [key: string]: any }> => { + return this.dataApi.checkHealth({ + sandboxId: this.sandboxId!, + config: params?.config, }); }; - /** - * Wait for sandbox to be ready (polls health check) - */ - waitUntilReady = async (params?: { - maxRetries?: number; - retryIntervalMs?: number; - }): Promise => { - const maxRetries = params?.maxRetries || 60; - const retryIntervalMs = params?.retryIntervalMs || 1000; - let retryCount = 0; - - logger.debug("Waiting for AIO sandbox to be ready..."); - - while (retryCount < maxRetries) { - retryCount += 1; - - try { - const health = await this.checkHealth(); - - if (health.status === "ok") { - logger.debug(`✓ AIO Sandbox is ready! (took ${retryCount} seconds)`); - return; - } - - logger.debug(`[${retryCount}/${maxRetries}] Health status: not ready`); - logger.debug( - `[${retryCount}/${maxRetries}] Health status: ${health.code} ${health.message}`, - ); - } catch (error) { - logger.error(`[${retryCount}/${maxRetries}] Health check failed: ${error}`); - } - - if (retryCount < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, retryIntervalMs)); - } - } - - throw new Error( - `Health check timeout after ${maxRetries} seconds. ` + - "AIO sandbox did not become ready in time.", - ); - }; - // ======================================== // Browser API Methods // ======================================== @@ -475,7 +439,10 @@ export class AioSandbox extends Sandbox { /** * Delete a recording */ - deleteRecording = async (params: { filename: string; config?: Config }): Promise => { + deleteRecording = async (params: { + filename: string; + config?: Config; + }): Promise => { return this.dataApi.deleteRecording(params); }; diff --git a/src/sandbox/api/control.ts b/src/sandbox/api/control.ts index 6949293..8040ce6 100644 --- a/src/sandbox/api/control.ts +++ b/src/sandbox/api/control.ts @@ -291,6 +291,50 @@ export class SandboxControlAPI extends ControlAPI { } }; + /** + * Delete Sandbox + * + * @param params - Method parameters + * @param params.sandboxId - Sandbox ID + * @param params.headers - Custom request headers + * @param params.config - Optional config override + * @returns Deleted Sandbox object + */ + deleteSandbox = async (params: { sandboxId: string; headers?: Record; config?: Config }): Promise<$AgentRun.Sandbox> => { + const { sandboxId, headers, config } = params; + + try { + const client = this.getClient(config); + const runtime = new $Util.RuntimeOptions({}); + + const response = await client.deleteSandboxWithOptions( + sandboxId, + headers ?? {}, + runtime + ); + + logger.debug( + `request api deleteSandbox, request Request ID: ${response.body?.requestId}\n request: ${JSON.stringify([sandboxId])}\n response: ${JSON.stringify(response.body?.data)}`, + ); + + return response.body?.data as $AgentRun.Sandbox; + } catch (error: unknown) { + if (error && typeof error === "object" && "statusCode" in error) { + const e = error as { statusCode: number; message: string; data?: { requestId?: string } }; + const statusCode = e.statusCode; + const message = e.message || "Unknown error"; + const requestId = e.data?.requestId; + + if (statusCode >= 400 && statusCode < 500) { + throw new ClientError(statusCode, message, { requestId }); + } else if (statusCode >= 500) { + throw new ServerError(statusCode, message, { requestId }); + } + } + throw error; + } + }; + /** * Stop Sandbox * diff --git a/src/sandbox/browser-sandbox.ts b/src/sandbox/browser-sandbox.ts index 6592155..eb7ac2e 100644 --- a/src/sandbox/browser-sandbox.ts +++ b/src/sandbox/browser-sandbox.ts @@ -5,17 +5,16 @@ * and recording management. */ -import { Config } from "../utils/config"; -import { logger } from "../utils/log"; +import { Config } from '../utils/config'; -import { BrowserDataAPI } from "./api/browser-data"; +import { BrowserDataAPI } from './api/browser-data'; import { NASConfig, OSSMountConfig, PolarFsConfig, TemplateType, -} from "./model"; -import { Sandbox } from "./sandbox"; +} from './model'; +import { Sandbox } from './sandbox'; /** * Browser Sandbox @@ -37,7 +36,7 @@ export class BrowserSandbox extends Sandbox { ossMountConfig?: OSSMountConfig; polarFsConfig?: PolarFsConfig; }, - config?: Config, + config?: Config ): Promise { const sandbox = await Sandbox.create( { @@ -47,7 +46,7 @@ export class BrowserSandbox extends Sandbox { ossMountConfig: options?.ossMountConfig, polarFsConfig: options?.polarFsConfig, }, - config, + config ); const browserSandbox = new BrowserSandbox(sandbox, config); @@ -66,7 +65,7 @@ export class BrowserSandbox extends Sandbox { get dataApi(): BrowserDataAPI { if (!this._dataApi) { if (!this.sandboxId) { - throw new Error("Sandbox ID is not set"); + throw new Error('Sandbox ID is not set'); } this._dataApi = new BrowserDataAPI({ @@ -80,50 +79,13 @@ export class BrowserSandbox extends Sandbox { /** * Check sandbox health */ - checkHealth = async (params?: { config?: Config }): Promise<{ status: string; [key: string]: any }> => { - return this.dataApi.checkHealth({ sandboxId: this.sandboxId!, config: params?.config }); - }; - - /** - * Wait for browser sandbox to be ready (polls health check) - */ - waitUntilReady = async (params?: { - maxRetries?: number; - retryIntervalMs?: number; - }): Promise => { - const maxRetries = params?.maxRetries || 60; - const retryIntervalMs = params?.retryIntervalMs || 1000; - let retryCount = 0; - - logger.debug("Waiting for browser to be ready..."); - - while (retryCount < maxRetries) { - retryCount += 1; - - try { - const health = await this.checkHealth(); - - if (health.status === "ok") { - logger.debug(`✓ Browser is ready! (took ${retryCount} seconds)`); - return; - } - - logger.debug( - `[${retryCount}/${maxRetries}] Health status: ${health.code} ${health.message}`, - ); - } catch (error) { - logger.error(`[${retryCount}/${maxRetries}] Health check failed: ${error}`); - } - - if (retryCount < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, retryIntervalMs)); - } - } - - throw new Error( - `Health check timeout after ${maxRetries} seconds. ` + - "Browser did not become ready in time.", - ); + checkHealth = async (params?: { + config?: Config; + }): Promise<{ status: string; [key: string]: any }> => { + return this.dataApi.checkHealth({ + sandboxId: this.sandboxId!, + config: params?.config, + }); }; /** @@ -140,7 +102,6 @@ export class BrowserSandbox extends Sandbox { return this.dataApi.getVncUrl(record); } - /** * List all recordings */ @@ -161,7 +122,10 @@ export class BrowserSandbox extends Sandbox { /** * Delete a recording */ - deleteRecording = async (params: { filename: string; config?: Config }): Promise => { + deleteRecording = async (params: { + filename: string; + config?: Config; + }): Promise => { return this.dataApi.deleteRecording(params); }; } diff --git a/src/sandbox/client.ts b/src/sandbox/client.ts index 8bf6610..d2e2d5c 100644 --- a/src/sandbox/client.ts +++ b/src/sandbox/client.ts @@ -5,10 +5,13 @@ * This module provides the client API for Sandbox. */ -import { Config } from "../utils/config"; +import * as $AgentRun from '@alicloud/agentrun20250910'; +import { Config } from '../utils/config'; +import { HTTPError } from '../utils/exception'; +import { SandboxControlAPI } from './api/control'; -import { BrowserSandbox } from "./browser-sandbox"; -import { CodeInterpreterSandbox } from "./code-interpreter-sandbox"; +import { BrowserSandbox } from './browser-sandbox'; +import { CodeInterpreterSandbox } from './code-interpreter-sandbox'; import { NASConfig, OSSMountConfig, @@ -17,11 +20,12 @@ import { SandboxListInput, TemplateCreateInput, TemplateListInput, + TemplateNetworkMode, TemplateType, TemplateUpdateInput, -} from "./model"; -import { Sandbox } from "./sandbox"; -import { Template } from "./template"; +} from './model'; +import { Sandbox } from './sandbox'; +import { Template } from './template'; /** * Sandbox Client @@ -30,9 +34,11 @@ import { Template } from "./template"; */ export class SandboxClient { private config?: Config; + private controlApi: SandboxControlAPI; constructor(config?: Config) { this.config = config; + this.controlApi = new SandboxControlAPI(config); } // ============ Template Operations ============ @@ -45,7 +51,30 @@ export class SandboxClient { config?: Config; }): Promise