Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions examples/mastra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Agent } from '@mastra/core/agent';
import { AgentRunServer, type AgentRequest } from '../src/server';
import {
MastraConverter,
type AgentEventItem,
model,
toolset,
} from '../src/integration/mastra';

import { logger } from '../src/utils/log';

const mastraAgent = new Agent({
id: 'run_agent',
name: 'AgentRun',
instructions: `
你是一个智能助手,你会帮助用户完成各种任务。你的输出后,必须是返向输出的。

如,用户输入 “你好”,应该输出 “?么的您助帮以可么什有,好您”
`.trim(),
model: () => model({ name: 'ohyee-test' }),
tools: () => toolset({ name: 'start-mcp-time-ggda' }),
});

async function* invokeAgent(
request: AgentRequest,
): AsyncGenerator<AgentEventItem> {
const converter = new MastraConverter();
const mastraStream = await mastraAgent.stream(
request.messages.map(
(msg) =>
({
role: msg.role,
content: msg.content || '',
}) as any,
Comment on lines +33 to +34
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of 'any' type is overly permissive here. The messages parameter should be typed more strictly according to Mastra's expected message format to ensure type safety and catch errors at compile time.

Copilot uses AI. Check for mistakes.
),
);
for await (const chunk of mastraStream.fullStream) {
const events = converter.convert(chunk);

for (const event of events) {
yield event;
}
}
}

const server = new AgentRunServer({
invokeAgent,
config: { corsOrigins: ['*'] },
});

logger.info(`
curl http://127.0.0.1:9000/openai/v1/chat/completions -X POST \\
-H "Content-Type: application/json" \\
-d \'{"messages": [{"role": "user", "content": "Hello!"}], "stream": true}\'

curl http://127.0.0.1:9000/ag-ui/agent -X POST \\
-H "Content-Type: application/json" \\
-d \'{"messages": [{"role": "user", "content": "Hello!"}]}\'
`);

server.start({ port: 9000 });
30 changes: 19 additions & 11 deletions examples/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
* npm run example:model
*/

import { ModelClient, ResourceAlreadyExistError, ResourceNotExistError, Status, BackendType, ModelType, ModelService, ModelProxy } from '../src/index';
import type { ModelServiceCreateInput, ModelServiceUpdateInput, ModelProxyCreateInput, ModelProxyUpdateInput, ProviderSettings, ProxyConfig } from '../src/index';
import { logger } from '../src/utils/log';
import type { ModelProxyCreateInput, ModelServiceCreateInput, ProviderSettings, ProxyConfig } from '../src/index';
import { ModelClient, ModelProxy, ModelService, ModelType, ResourceAlreadyExistError, ResourceNotExistError, Status } from '../src/index';
import { Config } from '../src/utils/config';
import { logger } from '../src/utils/log';

// Logger helper
function log(message: string, ...args: unknown[]) {
Expand Down Expand Up @@ -68,7 +68,7 @@ async function createOrGetModelService(): Promise<ModelService> {

// 等待就绪 / Wait for ready
await ms.waitUntilReadyOrFailed({
beforeCheck: (service: ModelService) =>
callback: (service) =>
log(` 当前状态 / Current status: ${service.status}`),
});

Expand Down Expand Up @@ -112,7 +112,11 @@ async function updateModelService(ms: ModelService): Promise<void> {
async function listModelServices(): Promise<void> {
log('枚举资源列表 / Listing resources');

const services = await ModelService.list({ modelType: ModelType.LLM });
const services = await ModelService.list({
input: {
modelType: ModelType.LLM
}
});
log(
`共有 ${services.length} 个资源,分别为 / Total ${services.length} resources:`,
services.map((s) => s.modelServiceName)
Expand All @@ -131,8 +135,10 @@ async function invokeModelService(ms: ModelService): Promise<void> {
});

// 流式输出 / Stream output
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
if ('textStream' in result && result.textStream) {
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
}
logger.info(''); // 换行
}
Expand Down Expand Up @@ -198,7 +204,7 @@ async function createOrGetModelProxy(): Promise<ModelProxy> {

// 等待就绪 / Wait for ready
await mp.waitUntilReadyOrFailed({
beforeCheck: (proxy: ModelProxy) =>
callback: (proxy) =>
log(` 当前状态 / Current status: ${proxy.status}`),
});

Expand Down Expand Up @@ -257,14 +263,16 @@ async function listModelProxies(): Promise<void> {
async function invokeModelProxy(mp: ModelProxy): Promise<void> {
log('调用模型代理进行推理 / Invoking model proxy for inference');

const result = await mp.completions({
const result = await mp.completion({
messages: [{ role: 'user', content: '你好,请介绍一下你自己' }],
stream: true,
});

// 流式输出 / Stream output
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
if ('textStream' in result && result.textStream) {
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
}
logger.info(''); // 换行
}
Expand Down
61 changes: 53 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,58 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./agent-runtime": {
"types": "./dist/agent-runtime/index.d.ts",
"import": "./dist/agent-runtime/index.js",
"require": "./dist/agent-runtime/index.cjs"
},
"./credential": {
"types": "./dist/credential/index.d.ts",
"import": "./dist/credential/index.js",
"require": "./dist/credential/index.cjs"
},
"./integration": {
"types": "./dist/integration/index.d.ts",
"import": "./dist/integration/index.js",
"require": "./dist/integration/index.cjs"
},
"./integration/mastra": {
"types": "./dist/integration/mastra/index.d.ts",
"import": "./dist/integration/mastra/index.js",
"require": "./dist/integration/mastra/index.cjs"
},
"./model": {
"types": "./dist/model/index.d.ts",
"import": "./dist/model/index.js",
"require": "./dist/model/index.cjs"
},
"./sandbox": {
"types": "./dist/sandbox/index.d.ts",
"import": "./dist/sandbox/index.js",
"require": "./dist/sandbox/index.cjs"
},
"./server": {
"types": "./dist/server/index.d.ts",
"import": "./dist/server/index.js",
"require": "./dist/server/index.cjs"
},
"./toolset": {
"types": "./dist/toolset/index.d.ts",
"import": "./dist/toolset/index.js",
"require": "./dist/toolset/index.cjs"
}
},
"files": [
"dist",
"README.md"
],
"scripts": {
"prebuild": "npm run generate-exports",
"build": "tsup",
"build:types": "tsc -p tsconfig.types.json",
"codegen": "npx tsx scripts/codegen.ts",
"generate-exports": "node scripts/generate-exports.mjs",
"format": "prettier --check \"src/**/*.{js,ts,jsx,tsx}\" --write",
"test": "jest",
"test:watch": "jest --watch",
Expand All @@ -30,6 +72,7 @@
"typecheck": "tsc --noEmit",
"prepublishOnly": "npm run build",
"example:quick-start": "npx tsx examples/quick-start.ts",
"example:quick-start-with-tools": "npx tsx examples/quick-start-with-tools.ts",
"example:agent-runtime": "npx tsx examples/agent-runtime.ts",
"example:credential": "npx tsx examples/credential.ts",
"example:sandbox": "npx tsx examples/sandbox.ts"
Expand Down Expand Up @@ -72,8 +115,17 @@
"uuid": "^9.0.0",
"zod": "^4.2.1"
},
"peerDependencies": {
"@mastra/core": "*"
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The peerDependencies section specifies "@mastra/core": "*" which accepts any version. This is risky as breaking changes in future versions of @mastra/core could break the integration. Consider specifying a more restrictive version range like "^1.0.0" based on the devDependency version.

Suggested change
"@mastra/core": "*"
"@mastra/core": "^1.0.0"

Copilot uses AI. Check for mistakes.
},
"peerDependenciesMeta": {
"@mastra/core": {
"optional": true
}
},
"devDependencies": {
"@happy-dom/global-registrator": "^15.0.0",
"@mastra/core": "^1.0.0",
"@types/archiver": "^7.0.0",
"@types/jest": "^29.5.0",
"@types/js-yaml": "^4.0.9",
Expand All @@ -84,18 +136,11 @@
"eslint": "^8.57.0",
"jest": "^29.7.0",
"jest-environment-node": "^29.7.0",
"playwright": "^1.57.0",
"ts-jest": "^29.2.0",
"tsup": "^8.3.0",
"tsx": "^4.19.0",
"typescript": "^5.4.0",
"yaml": "^2.7.0"
},
"peerDependencies": {
"@mastra/core": ">=0.5.0"
},
"peerDependenciesMeta": {
"@mastra/core": {
"optional": true
}
}
}
115 changes: 115 additions & 0 deletions scripts/generate-exports.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env node

/**
* Auto-generate package.json exports for sub-modules
*
* This script automatically scans the src directory for index.ts files
* and generates the corresponding exports in package.json
*
* Usage: node scripts/generate-exports.mjs
*/

import { readdirSync, statSync, readFileSync, writeFileSync } from 'fs';
import { join, resolve, dirname } from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const projectRoot = resolve(__dirname, '..');
const packageJsonPath = join(projectRoot, 'package.json');

// 扫描 src 目录下的所有 index.ts 文件
function getSubModules(srcDir) {
const modules = [];
const basePath = resolve(srcDir);

function scanDir(dir, relativePath = '') {
const items = readdirSync(dir);

for (const item of items) {
const fullPath = join(dir, item);
const stat = statSync(fullPath);

if (stat.isDirectory()) {
// 检查是否有 index.ts 文件
const indexPath = join(fullPath, 'index.ts');
try {
if (statSync(indexPath).isFile()) {
const modulePath = relativePath ? `${relativePath}/${item}` : item;
modules.push(modulePath);
}
} catch {
// index.ts 不存在,继续递归扫描子目录
}

// 递归扫描子目录
const newRelativePath = relativePath ? `${relativePath}/${item}` : item;
scanDir(fullPath, newRelativePath);
}
}
}

// 扫描 src 目录
scanDir(basePath);

// 过滤出用户可能想要导入的模块
// 排除一些内部目录,如 api, builtin, adapter, core, protocol, utils
const excludedDirs = ['api', 'builtin', 'adapter', 'core', 'protocol', 'utils'];
const mainModules = modules.filter(module => {
const parts = module.split('/');
const lastPart = parts[parts.length - 1];
return !excludedDirs.includes(lastPart);
});

return mainModules;
}

// 生成 exports 配置
function generateExports(modules) {
const exports = {
'.': {
types: './dist/index.d.ts',
import: './dist/index.js',
require: './dist/index.cjs'
}
};

for (const module of modules) {
exports[`./${module}`] = {
types: `./dist/${module}/index.d.ts`,
import: `./dist/${module}/index.js`,
require: `./dist/${module}/index.cjs`
};
}

return exports;
}

// 更新 package.json
function updatePackageJson() {
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
const subModules = getSubModules('src');
const newExports = generateExports(subModules);

console.log('Found sub-modules:');
subModules.forEach(mod => console.log(` - ${mod}`));

packageJson.exports = newExports;

writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');

console.log(`\nUpdated package.json exports with ${subModules.length} sub-modules`);
}

// 主函数
function main() {
try {
updatePackageJson();
console.log('✅ Exports generation completed successfully');
} catch (error) {
console.error('❌ Error generating exports:', error);
process.exit(1);
}
}

main();
Loading
Loading