Add UI automation test cases and webapp-testing skill

This commit is contained in:
2026-05-18 17:56:24 +08:00
parent eb053a347f
commit e0e22b895e
25 changed files with 3533 additions and 0 deletions

View File

@@ -0,0 +1 @@
# -*- coding:utf-8 -*-

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,600 @@
# Chat Completions API
**Source**: https://docs.routin.ai/zh/docs/API/chat-completions
**Description**: Chat Completions API 使用指南和多语言示例
---
[Routin AI](https://docs.routin.ai/zh/docs/API/</>)
[Routin AI](https://docs.routin.ai/zh/docs/API/</>)
搜索
`⌘``K`
🚀 欢迎使用 Routin AI 文档!
[前往控制台](https://docs.routin.ai/zh/docs/API/<https:/routin.ai/dashboard>)[欢迎使用 Routin AI](https://docs.routin.ai/zh/docs/API/</zh/docs>)
API 文档
[Chat Completions API](https://docs.routin.ai/zh/docs/API/</zh/docs/API/chat-completions>)[Embeddings API](https://docs.routin.ai/zh/docs/API/</zh/docs/API/embeddings>)[Images API](https://docs.routin.ai/zh/docs/API/</zh/docs/API/images>)[Audio API](https://docs.routin.ai/zh/docs/API/</zh/docs/API/audio>)[Messages API](https://docs.routin.ai/zh/docs/API/</zh/docs/API/messages>)[Gemini API](https://docs.routin.ai/zh/docs/API/</zh/docs/API/gemini>)[Videos API](https://docs.routin.ai/zh/docs/API/</zh/docs/API/video>)[Web Research API](https://docs.routin.ai/zh/docs/API/</zh/docs/API/web>)
设计方案
[套餐订阅制设计](https://docs.routin.ai/zh/docs/API/</zh/docs/Design/plan-subscription>)
开发者工具
[接入 Claude Code 使用](https://docs.routin.ai/zh/docs/API/</zh/docs/DeveloperTools/access-claude-code>)[接入 Codex 使用](https://docs.routin.ai/zh/docs/API/</zh/docs/DeveloperTools/access-codex>)[接入 Kilo Code 使用](https://docs.routin.ai/zh/docs/API/</zh/docs/DeveloperTools/access-kilo-code>)[接入 Cherry Studio 使用](https://docs.routin.ai/zh/docs/API/</zh/docs/DeveloperTools/access-cherry-studio>)[接入 Gemini CLI 使用](https://docs.routin.ai/zh/docs/API/</zh/docs/DeveloperTools/access-gemini>)[Claude Code 完整使用教程](https://docs.routin.ai/zh/docs/API/</zh/docs/DeveloperTools/access-claude-code-advanced>)[接入 OpenCode 使用](https://docs.routin.ai/zh/docs/API/</zh/docs/DeveloperTools/access-open-code>)
Organization
© 2026 Routin AI
Chat Completions API
API 文档
# Chat Completions API
Chat Completions API 使用指南和多语言示例
# Chat Completions API
MeteorAI 提供完全兼容 OpenAI 的对话接口,您可以使用 OpenAI SDK 直接调用我们的服务。
## 基本信息
**API 端点**
[code]
https://api.routin.ai/v1/chat/completions
[/code]
**认证方式** 在请求头中添加 API Key
[code]
Authorization: Bearer YOUR_API_KEY
[/code]
MeteorAI 完全兼容 OpenAI SDK只需修改 `base_url` 参数即可无缝切换。
## 请求参数
### 必需参数
参数| 类型| 说明
---|---|---
`model`| string| 模型名称,如 `gpt-4o``claude-3-5-sonnet-20241022`
`messages`| array| 对话消息数组
### 可选参数
参数| 类型| 默认值| 说明
---|---|---|---
`temperature`| number| 1| 采样温度 (0-2)
`top_p`| number| 1| 核采样参数 (0-1)
`max_tokens`| integer| -| 生成的最大 token 数
`stream`| boolean| false| 是否使用流式输出
`presence_penalty`| number| 0| 存在惩罚 (-2.0 到 2.0)
`frequency_penalty`| number| 0| 频率惩罚 (-2.0 到 2.0)
`user`| string| -| 用户标识符
### Messages 格式
[code]
{
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Hello!"
}
]
}
[/code]
支持的 `role` 值:
* `system`: 系统消息,定义助手行为
* `user`: 用户消息
* `assistant`: 助手回复
## 响应格式
### 普通响应
[code]
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-4o",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21
}
}
[/code]
### 流式响应
[code]
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
[/code]
## 代码示例
### 基本调用
PythonTypeScriptJavaScriptC#cURL
[code]
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.routin.ai/v1"
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "你好,介绍一下你自己"}
],
temperature=0.7,
max_tokens=150
)
print(response.choices[0].message.content)
[/code]
[code]
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://api.routin.ai/v1',
});
async function main() {
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: '你好,介绍一下你自己' },
],
temperature: 0.7,
max_tokens: 150,
});
console.log(response.choices[0].message.content);
}
main();
[/code]
[code]
const OpenAI = require('openai');
const client = new OpenAI({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://api.routin.ai/v1',
});
client.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: '你好,介绍一下你自己' },
],
temperature: 0.7,
max_tokens: 150,
}).then(response => {
console.log(response.choices[0].message.content);
});
[/code]
[code]
using OpenAI.Chat;
var client = new ChatClient(
model: "gpt-4o",
apiKey: "YOUR_API_KEY",
new OpenAIClientOptions
{
Endpoint = new Uri("https://api.routin.ai/v1")
}
);
var messages = new List<ChatMessage>
{
new SystemChatMessage("You are a helpful assistant."),
new UserChatMessage("你好,介绍一下你自己")
};
var response = await client.CompleteChatAsync(
messages,
new ChatCompletionOptions
{
Temperature = 0.7f,
MaxOutputTokenCount = 150
}
);
Console.WriteLine(response.Value.Content[0].Text);
[/code]
[code]
curl https://api.routin.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-4o",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "你好,介绍一下你自己"
}
],
"temperature": 0.7,
"max_tokens": 150
}'
[/code]
### 流式输出
流式输出可以实时获取模型的生成内容,提供更好的用户体验。
PythonTypeScriptJavaScriptC#cURL
[code]
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.routin.ai/v1"
)
stream = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": "给我讲一个有趣的故事"}
],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content is not None:
print(chunk.choices[0].delta.content, end="")
[/code]
[code]
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://api.routin.ai/v1',
});
async function main() {
const stream = await client.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: '给我讲一个有趣的故事' }],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
process.stdout.write(content);
}
}
}
main();
[/code]
[code]
const OpenAI = require('openai');
const client = new OpenAI({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://api.routin.ai/v1',
});
async function main() {
const stream = await client.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: '给我讲一个有趣的故事' }],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content;
if (content) {
process.stdout.write(content);
}
}
}
main();
[/code]
[code]
using OpenAI.Chat;
var client = new ChatClient(
model: "gpt-4o",
apiKey: "YOUR_API_KEY",
new OpenAIClientOptions
{
Endpoint = new Uri("https://api.routin.ai/v1")
}
);
var messages = new List<ChatMessage>
{
new UserChatMessage("给我讲一个有趣的故事")
};
await foreach (var update in client.CompleteChatStreamingAsync(messages))
{
foreach (var contentPart in update.ContentUpdate)
{
Console.Write(contentPart.Text);
}
}
[/code]
[code]
curl https://api.routin.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "给我讲一个有趣的故事"
}
],
"stream": true
}'
[/code]
### 多轮对话
PythonTypeScriptC#
[code]
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.routin.ai/v1"
)
messages = [
{"role": "system", "content": "You are a helpful assistant."}
]
# 第一轮对话
messages.append({"role": "user", "content": "我叫张三"})
response = client.chat.completions.create(
model="gpt-4o",
messages=messages
)
assistant_message = response.choices[0].message.content
messages.append({"role": "assistant", "content": assistant_message})
print(f"助手: {assistant_message}")
# 第二轮对话
messages.append({"role": "user", "content": "我叫什么名字?"})
response = client.chat.completions.create(
model="gpt-4o",
messages=messages
)
assistant_message = response.choices[0].message.content
print(f"助手: {assistant_message}")
[/code]
[code]
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://api.routin.ai/v1',
});
async function main() {
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: 'system', content: 'You are a helpful assistant.' },
];
// 第一轮对话
messages.push({ role: 'user', content: '我叫张三' });
let response = await client.chat.completions.create({
model: 'gpt-4o',
messages,
});
let assistantMessage = response.choices[0].message.content;
messages.push({ role: 'assistant', content: assistantMessage! });
console.log(`助手: ${assistantMessage}`);
// 第二轮对话
messages.push({ role: 'user', content: '我叫什么名字?' });
response = await client.chat.completions.create({
model: 'gpt-4o',
messages,
});
assistantMessage = response.choices[0].message.content;
console.log(`助手: ${assistantMessage}`);
}
main();
[/code]
[code]
using OpenAI.Chat;
var client = new ChatClient(
model: "gpt-4o",
apiKey: "YOUR_API_KEY",
new OpenAIClientOptions
{
Endpoint = new Uri("https://api.routin.ai/v1")
}
);
var messages = new List<ChatMessage>
{
new SystemChatMessage("You are a helpful assistant.")
};
// 第一轮对话
messages.Add(new UserChatMessage("我叫张三"));
var response = await client.CompleteChatAsync(messages);
var assistantMessage = response.Value.Content[0].Text;
messages.Add(new AssistantChatMessage(assistantMessage));
Console.WriteLine($"助手: {assistantMessage}");
// 第二轮对话
messages.Add(new UserChatMessage("我叫什么名字?"));
response = await client.CompleteChatAsync(messages);
assistantMessage = response.Value.Content[0].Text;
Console.WriteLine($"助手: {assistantMessage}");
[/code]
## 错误处理
请务必在生产环境中添加错误处理逻辑,避免因 API 调用失败导致应用崩溃。
PythonTypeScriptC#
[code]
from openai import OpenAI, APIError, RateLimitError, APIConnectionError
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.routin.ai/v1"
)
try:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)
except RateLimitError as e:
print(f"请求频率超限: {e}")
except APIConnectionError as e:
print(f"网络连接错误: {e}")
except APIError as e:
print(f"API 错误: {e}")
except Exception as e:
print(f"未知错误: {e}")
[/code]
[code]
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://api.routin.ai/v1',
});
async function main() {
try {
const response = await client.chat.completions.create({
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Hello!' }],
});
console.log(response.choices[0].message.content);
} catch (error) {
if (error instanceof OpenAI.APIError) {
console.error(`API 错误 [${error.status}]: ${error.message}`);
} else if (error instanceof OpenAI.RateLimitError) {
console.error('请求频率超限');
} else {
console.error('未知错误:', error);
}
}
}
main();
[/code]
[code]
using OpenAI.Chat;
using OpenAI;
var client = new ChatClient(
model: "gpt-4o",
apiKey: "YOUR_API_KEY",
new OpenAIClientOptions
{
Endpoint = new Uri("https://api.routin.ai/v1")
}
);
try
{
var response = await client.CompleteChatAsync(
new List<ChatMessage>
{
new UserChatMessage("Hello!")
}
);
Console.WriteLine(response.Value.Content[0].Text);
}
catch (ClientResultException ex) when (ex.Status == 429)
{
Console.WriteLine($"请求频率超限: {ex.Message}");
}
catch (ClientResultException ex)
{
Console.WriteLine($"API 错误 [{ex.Status}]: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"未知错误: {ex.Message}");
}
[/code]
## 常见错误码
错误码| 说明| 解决方法
---|---|---
401| API Key 无效或未提供| 检查 Authorization 头是否正确设置
429| 请求频率超限| 降低请求频率或升级配额
400| 请求参数错误| 检查请求参数格式是否正确
500| 服务器内部错误| 稍后重试或联系技术支持
503| 服务暂时不可用| 稍后重试
## 最佳实践
1. **使用系统消息** : 通过 `system` 角色定义助手的行为和特性
2. **控制 token 数量** : 使用 `max_tokens` 参数控制生成长度,避免不必要的费用
3. **错误重试** : 实现指数退避的重试机制,处理临时性错误
4. **流式输出** : 对于长文本生成,使用 `stream=true` 提供更好的用户体验
5. **保存对话历史** : 多轮对话需要在 `messages` 数组中包含完整的对话历史
6. **监控使用情况** : 定期查看管理后台的统计信息,优化 API 使用
## 更多资源
* [Embeddings API](https://docs.routin.ai/zh/docs/API/</zh/API/embeddings>) \- 文本向量化接口
* [Images API](https://docs.routin.ai/zh/docs/API/</zh/API/images>) \- 图像生成接口
* [Audio API](https://docs.routin.ai/zh/docs/API/</zh/API/audio>) \- 语音识别和合成接口
[欢迎使用 Routin AI统一的大模型 API 聚合平台,提供企业级服务和管理能力](https://docs.routin.ai/zh/docs/API/</zh/docs>)[Embeddings API文本向量化 API 使用指南和多语言示例](https://docs.routin.ai/zh/docs/API/</zh/docs/API/embeddings>)
### On this page
Chat Completions API基本信息请求参数必需参数可选参数Messages 格式响应格式普通响应流式响应代码示例基本调用流式输出多轮对话错误处理常见错误码最佳实践更多资源

View File

@@ -0,0 +1,177 @@
# -*- coding:utf-8 -*-
import argparse
import json
import traceback
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlparse
from base_framework.platform_tools.Create_ui_testcase.generate_automation_api import (
generate_save_and_verify_ui_testcase
)
API_PATH = "/it/api/case/generate-automation"
class GenerateAutomationHandler(BaseHTTPRequestHandler):
def do_OPTIONS(self):
self._send_json_response(200, {"code": 0, "message": "ok", "data": None})
def do_POST(self):
request_path = urlparse(self.path).path
if request_path != API_PATH:
self._send_json_response(404, {
"code": 404,
"message": "接口不存在:{0}".format(request_path),
"data": None
})
return
try:
request_data = self._read_json_body()
self._validate_required_fields(request_data)
if request_data.get("automationType") != "ui":
self._send_json_response(200, {
"code": 1,
"message": "automationType不是ui不调用UI自动化用例生成接口",
"data": {
"projectId": request_data.get("projectId"),
"caseId": request_data.get("caseId"),
"automationType": request_data.get("automationType"),
"caseKey": request_data.get("caseKey"),
"content": self._get_case_name(request_data)
}
})
return
verify_result = generate_save_and_verify_ui_testcase(
project_id=request_data.get("projectId"),
case_id=request_data.get("caseId"),
automation_type=request_data.get("automationType"),
prompt=request_data.get("prompt"),
case_key=request_data.get("caseKey"),
module_name=request_data.get("moduleName"),
product_name=request_data.get("productName"),
project_name=request_data.get("projectName"),
steps=request_data.get("steps"),
expected_results=request_data.get("expectedResults"),
case_name=self._get_case_name(request_data),
enable_reconnaissance=request_data.get("enableReconnaissance", True),
headless=request_data.get("headless", True),
max_attempts=request_data.get("maxAttempts", 3)
)
if not verify_result.get("success"):
self._send_json_response(200, {
"code": 1,
"message": "用例生成后执行失败,自动修复{0}次仍未通过".format(request_data.get("maxAttempts", 3)),
"data": {
"projectId": request_data.get("projectId"),
"caseId": request_data.get("caseId"),
"automationType": request_data.get("automationType"),
"caseKey": request_data.get("caseKey"),
"content": self._get_case_name(request_data),
"testcasePath": verify_result.get("testcasePath"),
"attempts": verify_result.get("attempts"),
"failureReason": verify_result.get("failureReason")
}
})
return
self._send_json_response(200, {
"code": 0,
"message": "success",
"data": {
"projectId": request_data.get("projectId"),
"caseId": request_data.get("caseId"),
"automationType": request_data.get("automationType"),
"caseKey": request_data.get("caseKey"),
"content": self._get_case_name(request_data),
"testcasePath": verify_result.get("testcasePath"),
"attempts": verify_result.get("attempts")
}
})
except ValueError as error:
self._send_json_response(400, {
"code": 400,
"message": str(error),
"data": None
})
except Exception as error:
self._send_json_response(500, {
"code": 500,
"message": str(error),
"data": None,
"trace": traceback.format_exc()
})
def _read_json_body(self):
content_length = int(self.headers.get("Content-Length", 0))
if content_length <= 0:
raise ValueError("请求体不能为空")
body = self.rfile.read(content_length).decode("utf-8")
try:
return json.loads(body)
except ValueError:
raise ValueError("请求体必须是合法JSON")
def _validate_required_fields(self, request_data):
required_fields = [
"projectId",
"caseId",
"automationType",
"prompt",
"caseKey",
"moduleName",
"productName",
"projectName",
"steps",
"expectedResults"
]
missing_fields = []
for field in required_fields:
if field == "productName":
if field not in request_data or request_data.get(field) is None:
missing_fields.append(field)
elif request_data.get(field) in (None, ""):
missing_fields.append(field)
if missing_fields:
raise ValueError("缺少必填参数:{0}".format(", ".join(missing_fields)))
def _get_case_name(self, request_data):
return request_data.get("caseName") or request_data.get("title") or request_data.get("name") or "{0}-{1}".format(
request_data.get("caseKey"),
request_data.get("moduleName")
)
def _send_json_response(self, status_code, response_data):
response_body = json.dumps(response_data, ensure_ascii=False).encode("utf-8")
self.send_response(status_code)
self.send_header("Content-Type", "application/json;charset=UTF-8")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type, accessToken, Authorization")
self.send_header("Content-Length", str(len(response_body)))
self.end_headers()
self.wfile.write(response_body)
def log_message(self, format, *args):
print("[{0}] {1}".format(self.log_date_time_string(), format % args))
def run_server(host="0.0.0.0", port=8081):
server = ThreadingHTTPServer((host, port), GenerateAutomationHandler)
print("Create_ui_testcase HTTP服务已启动http://{0}:{1}".format(host, port))
print("接口地址POST {0}".format(API_PATH))
server.serve_forever()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="UI自动化用例生成HTTP服务")
parser.add_argument("--host", default="0.0.0.0", help="服务监听地址默认0.0.0.0")
parser.add_argument("--port", default=8081, type=int, help="服务监听端口默认8081")
args = parser.parse_args()
run_server(host=args.host, port=args.port)