Skip to content

Commit 16f4994

Browse files
committed
feat: 🎉 support serve astack pipeline or standalone component & streaming agent
1 parent aec0783 commit 16f4994

37 files changed

+6096
-952
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,7 @@ coverage
5656
# 临时文件
5757
tmp
5858
temp
59+
60+
# claude code
61+
.claude/
62+
.serena/

examples/serve-astack/Dockerfile

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Multi-stage build for AStack Chat Server
2+
FROM node:18-alpine AS base
3+
4+
# Install pnpm
5+
RUN npm install -g pnpm@8.0.0
6+
7+
WORKDIR /app
8+
9+
# Copy workspace configuration
10+
COPY pnpm-workspace.yaml package.json ./
11+
12+
# Copy all package.json files
13+
COPY backend/package.json backend/
14+
COPY frontend/package.json frontend/
15+
16+
# Install dependencies
17+
RUN pnpm install --frozen-lockfile
18+
19+
# Copy source code
20+
COPY backend/ backend/
21+
COPY frontend/ frontend/
22+
23+
# Build applications
24+
RUN pnpm build
25+
26+
# Production stage
27+
FROM node:18-alpine AS production
28+
29+
RUN npm install -g pnpm@8.0.0
30+
31+
WORKDIR /app
32+
33+
# Copy built applications
34+
COPY --from=base /app/backend/dist ./backend/dist
35+
COPY --from=base /app/frontend/.next ./frontend/.next
36+
COPY --from=base /app/backend/package.json ./backend/
37+
COPY --from=base /app/frontend/package.json ./frontend/
38+
COPY --from=base /app/package.json ./
39+
COPY --from=base /app/pnpm-workspace.yaml ./
40+
41+
# Copy necessary frontend static files
42+
COPY --from=base /app/frontend/public ./frontend/public
43+
COPY --from=base /app/frontend/next.config.ts ./frontend/
44+
45+
# Install production dependencies only
46+
RUN pnpm install --prod --frozen-lockfile
47+
48+
# Create non-root user
49+
RUN addgroup -g 1001 -S nodejs
50+
RUN adduser -S nextjs -u 1001
51+
52+
# Change ownership
53+
RUN chown -R nextjs:nodejs /app
54+
USER nextjs
55+
56+
# Expose ports
57+
EXPOSE 3000 8080
58+
59+
# Health check
60+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
61+
CMD curl -f http://localhost:8080/api/health || exit 1
62+
63+
# Start both services
64+
CMD ["pnpm", "start"]

examples/serve-astack/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### About
2+
3+
> AStack Server Wrapper Example
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# AStack Chat Server - 后端环境变量配置
2+
3+
# ===== LLM 模型配置 =====
4+
# Deepseek API Key (推荐)
5+
DEEPSEEK_API_KEY=your_deepseek_api_key_here
6+
7+
# OpenAI API Key (可选)
8+
# OPENAI_API_KEY=your_openai_api_key_here
9+
10+
# ===== 服务器配置 =====
11+
# 服务器端口,默认 8080
12+
PORT=8080
13+
14+
# 服务器绑定地址,默认 0.0.0.0
15+
HOST=0.0.0.0
16+
17+
# ===== 日志配置 =====
18+
# 日志级别: trace, debug, info, warn, error, fatal
19+
LOG_LEVEL=info
20+
21+
# ===== CORS 配置 =====
22+
# 允许的前端域名,用逗号分隔
23+
# ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001
24+
25+
# ===== Agent 配置 =====
26+
# 数学 Agent 温度参数(0-1)
27+
MATH_AGENT_TEMPERATURE=0.3
28+
29+
# 文本分析 Agent 温度参数(0-1)
30+
TEXT_AGENT_TEMPERATURE=0.7
31+
32+
# 普通聊天温度参数(0-1)
33+
CHAT_TEMPERATURE=0.7
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "serve-astack-backend",
3+
"version": "1.0.0",
4+
"description": "Backend for the AStack server example",
5+
"type": "module",
6+
"main": "dist/server.js",
7+
"scripts": {
8+
"build": "tsc -p tsconfig.json",
9+
"start": "node dist/server.js",
10+
"dev": "tsx watch src/server.ts",
11+
"lint": "eslint src --ext .ts",
12+
"type-check": "tsc --noEmit"
13+
},
14+
"dependencies": {
15+
"@astack-tech/components": "workspace:*",
16+
"@astack-tech/core": "workspace:*",
17+
"@astack-tech/integrations": "workspace:*",
18+
"@astack-tech/tools": "workspace:*",
19+
"@fastify/type-provider-typebox": "^4.1.0",
20+
"@sinclair/typebox": "^0.34.41",
21+
"dotenv": "^17.2.2",
22+
"fastify": "^5.2.0"
23+
},
24+
"devDependencies": {
25+
"@types/node": "^22.0.0",
26+
"tsx": "^4.19.0",
27+
"typescript": "^5.6.0"
28+
}
29+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { StreamingAgent } from '@astack-tech/components';
2+
import { createTool } from '@astack-tech/tools';
3+
import { Deepseek } from '@astack-tech/integrations/model-provider';
4+
import type { ModelProvider } from '@astack-tech/components';
5+
6+
// 计算器工具(与原math-agent完全一致)
7+
const calculatorTool = createTool(
8+
'calculator',
9+
'执行基本的数学计算',
10+
async (args: Record<string, unknown>) => {
11+
const { expression } = args as { expression: string };
12+
try {
13+
// 安全的数学计算,只允许基本数学运算
14+
const sanitized = expression.replace(/[^0-9+\-*/(). ]/g, '');
15+
const result = Function(`"use strict"; return (${sanitized})`)();
16+
return `计算结果: ${expression} = ${result}`;
17+
} catch (error) {
18+
return `计算错误: ${error instanceof Error ? error.message : String(error)}`;
19+
}
20+
},
21+
{
22+
type: 'object',
23+
properties: {
24+
expression: {
25+
type: 'string',
26+
description: '数学表达式,如 "2 + 3" 或 "10 * 5"',
27+
},
28+
},
29+
required: ['expression'],
30+
}
31+
);
32+
33+
// 创建流式数学助手 Agent
34+
export function createStreamingMathAgent(): StreamingAgent {
35+
const apiKey = process.env.DEEPSEEK_API_KEY || '';
36+
if (!apiKey) {
37+
throw new Error('DEEPSEEK_API_KEY environment variable is required');
38+
}
39+
40+
const model = new Deepseek({
41+
apiKey,
42+
model: 'deepseek-chat',
43+
temperature: 0.3,
44+
});
45+
46+
return new StreamingAgent({
47+
model: model as ModelProvider,
48+
tools: [calculatorTool],
49+
systemPrompt: `你是一个专业的数学助手,专门帮助用户进行数学计算和解答数学问题。
50+
51+
你的能力:
52+
- 执行基本的数学运算(加减乘除)
53+
- 计算复杂的数学表达式
54+
- 解答数学问题和提供解题思路
55+
56+
当用户提到需要计算时,请使用 calculator 工具来准确计算结果。
57+
计算完成后,请给出清晰的解答和必要的说明。
58+
59+
可用工具:
60+
1. calculator - 执行基本的数学计算
61+
参数:
62+
- expression: 数学表达式,如 "2 + 3" 或 "10 * 5"`,
63+
verbose: true,
64+
maxIterations: 3,
65+
});
66+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { StreamingAgent } from '@astack-tech/components';
2+
import { createTool } from '@astack-tech/tools';
3+
import { Deepseek } from '@astack-tech/integrations/model-provider';
4+
import type { ModelProvider } from '@astack-tech/components';
5+
6+
// 文本分析工具(与原text-agent完全一致)
7+
const textAnalysisTool = createTool(
8+
'textAnalysis',
9+
'分析文本内容,统计字数、词数等信息',
10+
async (args: Record<string, unknown>) => {
11+
const { text } = args as { text: string };
12+
13+
const charCount = text.length;
14+
const wordCount = text.split(/\s+/).filter(word => word.length > 0).length;
15+
const lineCount = text.split('\n').length;
16+
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0).length;
17+
18+
return {
19+
原文: text,
20+
字符数: charCount,
21+
词数: wordCount,
22+
行数: lineCount,
23+
句数: sentences,
24+
分析摘要: `这段文本包含 ${charCount} 个字符、${wordCount} 个词、${lineCount} 行、${sentences} 个句子。`,
25+
};
26+
},
27+
{
28+
type: 'object',
29+
properties: {
30+
text: {
31+
type: 'string',
32+
description: '需要分析的文本内容',
33+
},
34+
},
35+
required: ['text'],
36+
}
37+
);
38+
39+
// 创建流式文本分析助手 Agent
40+
export function createStreamingTextAgent(): StreamingAgent {
41+
const apiKey = process.env.DEEPSEEK_API_KEY || '';
42+
if (!apiKey) {
43+
throw new Error('DEEPSEEK_API_KEY environment variable is required');
44+
}
45+
46+
const model = new Deepseek({
47+
apiKey,
48+
model: 'deepseek-chat',
49+
temperature: 0.7,
50+
});
51+
52+
return new StreamingAgent({
53+
model: model as ModelProvider,
54+
tools: [textAnalysisTool],
55+
systemPrompt: `你是一个专业的文本分析助手,专门帮助用户分析和理解文本内容。
56+
57+
你的能力:
58+
- 分析文本的基本统计信息(字数、词数、行数等)
59+
- 提供文本内容的深度分析
60+
- 给出文本改进建议
61+
62+
当用户需要分析文本时,请使用 textAnalysis 工具获取详细的统计信息。
63+
分析完成后,请提供有见地的分析报告和实用的建议。`,
64+
verbose: true,
65+
maxIterations: 3,
66+
});
67+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { createStreamingMathAgent } from './StreamingMathAgent.js';
2+
import { createStreamingTextAgent } from './StreamingTextAgent.js';
3+
import type { IntentType } from '../types/index.js';
4+
5+
// Streaming Agent 实例缓存
6+
let streamingMathAgent: ReturnType<typeof createStreamingMathAgent> | null = null;
7+
let streamingTextAgent: ReturnType<typeof createStreamingTextAgent> | null = null;
8+
9+
// 获取 Streaming Math Agent
10+
export function getStreamingMathAgent() {
11+
if (!streamingMathAgent) {
12+
streamingMathAgent = createStreamingMathAgent();
13+
}
14+
return streamingMathAgent;
15+
}
16+
17+
// 获取 Streaming Text Agent
18+
export function getStreamingTextAgent() {
19+
if (!streamingTextAgent) {
20+
streamingTextAgent = createStreamingTextAgent();
21+
}
22+
return streamingTextAgent;
23+
}
24+
25+
// 根据意图类型获取对应的 Streaming Agent
26+
export function getStreamingAgentByIntent(intent: IntentType) {
27+
switch (intent) {
28+
case 'math':
29+
return getStreamingMathAgent();
30+
case 'text':
31+
return getStreamingTextAgent();
32+
default:
33+
return null;
34+
}
35+
}
36+
37+
// 简单的意图分类器
38+
export function classifyIntent(message: string): IntentType {
39+
const lowerMessage = message.toLowerCase();
40+
41+
// 数学关键词
42+
const mathKeywords = [
43+
'计算',
44+
'算',
45+
'数学',
46+
'加',
47+
'减',
48+
'乘',
49+
'除',
50+
'+',
51+
'-',
52+
'*',
53+
'/',
54+
'=',
55+
'等于',
56+
'求解',
57+
'平方',
58+
'开方',
59+
'幂',
60+
'次方',
61+
];
62+
63+
// 文本分析关键词
64+
const textKeywords = [
65+
'分析文本',
66+
'统计字数',
67+
'分析内容',
68+
'文本分析',
69+
'字符数',
70+
'词数',
71+
'文本统计',
72+
'内容分析',
73+
];
74+
75+
// 检查数学关键词
76+
if (mathKeywords.some(keyword => lowerMessage.includes(keyword))) {
77+
return 'math';
78+
}
79+
80+
// 检查文本分析关键词
81+
if (textKeywords.some(keyword => lowerMessage.includes(keyword))) {
82+
return 'text';
83+
}
84+
85+
// 检查是否包含明显的数学表达式
86+
if (/\d+\s*[+\-*/]\s*\d+/.test(message)) {
87+
return 'math';
88+
}
89+
90+
// 默认是普通聊天
91+
return 'chat';
92+
}

0 commit comments

Comments
 (0)