Compare commits
4 Commits
EXT_v0.1.2
..
v1.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
| d00a8dcc21 | |||
| 851e9ef158 | |||
| 8ea8ce9acd | |||
| 9bd4a47d35 |
+3
-3
@@ -40,7 +40,7 @@
|
||||
|
||||
```html
|
||||
<script
|
||||
src="https://registry.npmmirror.com/page-agent/1.1.0/files/dist/iife/page-agent.demo.js"
|
||||
src="https://registry.npmmirror.com/page-agent/1.1.1/files/dist/iife/page-agent.demo.js"
|
||||
crossorigin="true"
|
||||
></script>
|
||||
```
|
||||
@@ -50,8 +50,8 @@
|
||||
|
||||
| Mirrors | URL |
|
||||
| ------- | ---------------------------------------------------------------------------------- |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.1.0/dist/iife/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/1.1.0/files/dist/iife/page-agent.demo.js |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.1.1/dist/iife/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/1.1.1/files/dist/iife/page-agent.demo.js |
|
||||
|
||||
### NPM 安装
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ Fastest way to try PageAgent with our free Demo LLM:
|
||||
|
||||
```html
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/page-agent@1.1.0/dist/iife/page-agent.demo.js"
|
||||
src="https://cdn.jsdelivr.net/npm/page-agent@1.1.1/dist/iife/page-agent.demo.js"
|
||||
crossorigin="true"
|
||||
></script>
|
||||
```
|
||||
@@ -50,8 +50,8 @@ Fastest way to try PageAgent with our free Demo LLM:
|
||||
|
||||
| Mirrors | URL |
|
||||
| ------- | ---------------------------------------------------------------------------------- |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.1.0/dist/iife/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/1.1.0/files/dist/iife/page-agent.demo.js |
|
||||
| Global | https://cdn.jsdelivr.net/npm/page-agent@1.1.1/dist/iife/page-agent.demo.js |
|
||||
| China | https://registry.npmmirror.com/page-agent/1.1.1/files/dist/iife/page-agent.demo.js |
|
||||
|
||||
### NPM Installation
|
||||
|
||||
|
||||
Generated
+22
-22
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "root",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "root",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
@@ -1639,9 +1639,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/brace-expansion": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz",
|
||||
"integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -11049,24 +11049,24 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@page-agent/core",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@page-agent/llms": "1.1.0",
|
||||
"@page-agent/page-controller": "1.1.0",
|
||||
"@page-agent/llms": "1.1.1",
|
||||
"@page-agent/page-controller": "1.1.1",
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5"
|
||||
}
|
||||
},
|
||||
"packages/extension": {
|
||||
"name": "@page-agent/ext",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@page-agent/core": "1.1.0",
|
||||
"@page-agent/llms": "1.1.0",
|
||||
"@page-agent/page-controller": "1.1.0",
|
||||
"@page-agent/ui": "1.1.0",
|
||||
"@page-agent/core": "1.1.1",
|
||||
"@page-agent/llms": "1.1.1",
|
||||
"@page-agent/page-controller": "1.1.1",
|
||||
"@page-agent/ui": "1.1.1",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
@@ -11100,7 +11100,7 @@
|
||||
},
|
||||
"packages/llms": {
|
||||
"name": "@page-agent/llms",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
@@ -11108,20 +11108,20 @@
|
||||
}
|
||||
},
|
||||
"packages/page-agent": {
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@page-agent/core": "1.1.0",
|
||||
"@page-agent/llms": "1.1.0",
|
||||
"@page-agent/page-controller": "1.1.0",
|
||||
"@page-agent/ui": "1.1.0",
|
||||
"@page-agent/core": "1.1.1",
|
||||
"@page-agent/llms": "1.1.1",
|
||||
"@page-agent/page-controller": "1.1.1",
|
||||
"@page-agent/ui": "1.1.1",
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5"
|
||||
}
|
||||
},
|
||||
"packages/page-controller": {
|
||||
"name": "@page-agent/page-controller",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ai-motion": "^0.4.8"
|
||||
@@ -11129,12 +11129,12 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@page-agent/ui",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"packages/website": {
|
||||
"name": "@page-agent/website",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"packages/page-controller",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@page-agent/core",
|
||||
"private": false,
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"main": "./dist/esm/page-agent-core.js",
|
||||
"module": "./dist/esm/page-agent-core.js",
|
||||
@@ -45,7 +45,7 @@
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5",
|
||||
"@page-agent/llms": "1.1.0",
|
||||
"@page-agent/page-controller": "1.1.0"
|
||||
"@page-agent/llms": "1.1.1",
|
||||
"@page-agent/page-controller": "1.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,12 +144,12 @@ Here are examples of good output patterns. Use them as reference but never copy
|
||||
</examples>
|
||||
|
||||
<output>
|
||||
You must ALWAYS respond with a valid JSON in this exact format:
|
||||
|
||||
{
|
||||
"evaluation_previous_goal": "Concise one-sentence analysis of your last action. Clearly state success, failure, or uncertain.",
|
||||
"memory": "1-3 concise sentences of specific memory of this step and overall progress. You should put here everything that will help you track progress in future steps. Like counting pages visited, items found, etc.",
|
||||
"next_goal": "State the next immediate goal and action to achieve it, in one clear sentence."
|
||||
"action":{"action": {// action-specific parameter}}
|
||||
"next_goal": "State the next immediate goal and action to achieve it, in one clear sentence.",
|
||||
"action":{
|
||||
"Action name": {// Action parameters}
|
||||
}
|
||||
}
|
||||
</output>
|
||||
|
||||
@@ -33,7 +33,6 @@ import type {
|
||||
AgentStatus,
|
||||
ExecutionResult,
|
||||
HistoricalEvent,
|
||||
LLMConfig,
|
||||
} from '@page-agent/core'
|
||||
|
||||
// Wait for extension injection (up to 1 second)
|
||||
@@ -48,18 +47,13 @@ async function waitForExtension(timeout = 1000): Promise<boolean> {
|
||||
|
||||
// Usage
|
||||
if (await waitForExtension()) {
|
||||
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||
'Click the login button',
|
||||
{
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: 'your-api-key',
|
||||
model: 'gpt-5.2',
|
||||
},
|
||||
{
|
||||
onStatusChange: (status) => console.log('Status:', status),
|
||||
onActivity: (activity) => console.log('Activity:', activity),
|
||||
}
|
||||
)
|
||||
const result = await window.PAGE_AGENT_EXT!.execute('Click the login button', {
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: 'your-api-key',
|
||||
model: 'gpt-5.2',
|
||||
onStatusChange: (status) => console.log('Status:', status),
|
||||
onActivity: (activity) => console.log('Activity:', activity),
|
||||
})
|
||||
console.log('Result:', result)
|
||||
}
|
||||
```
|
||||
@@ -76,7 +70,7 @@ Extension version string (e.g., `"1.0.0"`). This is exposed separately to allow
|
||||
|
||||
Main API namespace object containing:
|
||||
|
||||
#### `PAGE_AGENT_EXT.execute(task, llmConfig, hooks?)`
|
||||
#### `PAGE_AGENT_EXT.execute(task, config)`
|
||||
|
||||
Execute an agent task.
|
||||
|
||||
@@ -85,8 +79,7 @@ Execute an agent task.
|
||||
| Name | Type | Required | Description |
|
||||
|------|------|----------|-------------|
|
||||
| `task` | `string` | Yes | Task description |
|
||||
| `llmConfig` | `LLMConfig` | Yes | LLM configuration |
|
||||
| `hooks` | `ExecuteHooks` | No | Event callbacks |
|
||||
| `config` | `ExecuteConfig` | Yes | Execution configuration (LLM settings, options, and event callbacks) |
|
||||
|
||||
**Returns:** `Promise<ExecutionResult>`
|
||||
|
||||
@@ -104,21 +97,26 @@ import type {
|
||||
AgentStatus,
|
||||
ExecutionResult,
|
||||
HistoricalEvent,
|
||||
LLMConfig,
|
||||
} from '@page-agent/core'
|
||||
|
||||
export interface ExecuteHooks {
|
||||
export interface ExecuteConfig {
|
||||
baseURL: string
|
||||
apiKey: string
|
||||
model: string
|
||||
|
||||
/**
|
||||
* Whether to include the initial tab (that holds this main world script) in the task.
|
||||
* @default true
|
||||
*/
|
||||
includeInitialTab?: boolean
|
||||
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
onActivity?: (activity: AgentActivity) => void
|
||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||
onDispose?: () => void
|
||||
}
|
||||
|
||||
export type Execute = (
|
||||
task: string,
|
||||
llmConfig: LLMConfig,
|
||||
hooks?: ExecuteHooks
|
||||
) => Promise<ExecutionResult>
|
||||
export type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||
```
|
||||
|
||||
### AgentStatus
|
||||
@@ -164,16 +162,6 @@ type HistoricalEvent =
|
||||
| { type: 'error'; message: string; rawResponse?: unknown }
|
||||
```
|
||||
|
||||
### LLMConfig
|
||||
|
||||
```typescript
|
||||
interface LLMConfig {
|
||||
baseURL: string // e.g. 'https://api.openai.com/v1'
|
||||
apiKey: string
|
||||
model: string // e.g. 'gpt-5.2'
|
||||
}
|
||||
```
|
||||
|
||||
### ExecutionResult
|
||||
|
||||
```typescript
|
||||
@@ -205,44 +193,63 @@ if (result.success) {
|
||||
}
|
||||
```
|
||||
|
||||
### With Event Hooks
|
||||
### Exclude Initial Tab
|
||||
|
||||
By default, the agent includes the initial tab (where the script runs) in the task. Set `includeInitialTab: false` to exclude it:
|
||||
|
||||
```typescript
|
||||
await window.PAGE_AGENT_EXT!.execute(
|
||||
'Navigate to the settings page',
|
||||
llmConfig,
|
||||
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||
'Open a new tab and search for page-agent on GitHub',
|
||||
{
|
||||
onStatusChange: (status) => {
|
||||
updateUI({ agentStatus: status })
|
||||
},
|
||||
onActivity: (activity) => {
|
||||
switch (activity.type) {
|
||||
case 'thinking':
|
||||
showSpinner('Agent is thinking...')
|
||||
break
|
||||
case 'executing':
|
||||
showSpinner(`Executing: ${activity.tool}`)
|
||||
break
|
||||
case 'executed':
|
||||
log(`${activity.tool} completed in ${activity.duration}ms`)
|
||||
break
|
||||
case 'error':
|
||||
showError(activity.message)
|
||||
break
|
||||
}
|
||||
},
|
||||
onHistoryUpdate: (history) => {
|
||||
renderHistory(history)
|
||||
},
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
model: 'gpt-5.2',
|
||||
includeInitialTab: false, // Agent will open new tabs only
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### With Event Callbacks
|
||||
|
||||
```typescript
|
||||
await window.PAGE_AGENT_EXT!.execute('Navigate to the settings page', {
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
model: 'gpt-5.2',
|
||||
onStatusChange: (status) => {
|
||||
updateUI({ agentStatus: status })
|
||||
},
|
||||
onActivity: (activity) => {
|
||||
switch (activity.type) {
|
||||
case 'thinking':
|
||||
showSpinner('Agent is thinking...')
|
||||
break
|
||||
case 'executing':
|
||||
showSpinner(`Executing: ${activity.tool}`)
|
||||
break
|
||||
case 'executed':
|
||||
log(`${activity.tool} completed in ${activity.duration}ms`)
|
||||
break
|
||||
case 'error':
|
||||
showError(activity.message)
|
||||
break
|
||||
}
|
||||
},
|
||||
onHistoryUpdate: (history) => {
|
||||
renderHistory(history)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Stop Execution
|
||||
|
||||
```typescript
|
||||
// Start a task
|
||||
window.PAGE_AGENT_EXT!.execute('Scroll through all pages', llmConfig)
|
||||
window.PAGE_AGENT_EXT!.execute('Scroll through all pages', {
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
model: 'gpt-5.2',
|
||||
})
|
||||
|
||||
// Later, stop it
|
||||
window.PAGE_AGENT_EXT!.dispose()
|
||||
@@ -258,24 +265,25 @@ import type {
|
||||
AgentStatus,
|
||||
ExecutionResult,
|
||||
HistoricalEvent,
|
||||
LLMConfig,
|
||||
} from '@page-agent/core'
|
||||
|
||||
interface ExecuteConfig {
|
||||
baseURL: string
|
||||
apiKey: string
|
||||
model: string
|
||||
includeInitialTab?: boolean
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
onActivity?: (activity: AgentActivity) => void
|
||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||
onDispose?: () => void
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
PAGE_AGENT_EXT_VERSION?: string
|
||||
PAGE_AGENT_EXT?: {
|
||||
version: string
|
||||
execute: (
|
||||
task: string,
|
||||
llmConfig: LLMConfig,
|
||||
hooks?: {
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
onActivity?: (activity: AgentActivity) => void
|
||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||
onDispose?: () => void
|
||||
}
|
||||
) => Promise<ExecutionResult>
|
||||
execute: (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||
dispose: () => void
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import type {
|
||||
AgentStatus,
|
||||
ExecutionResult,
|
||||
HistoricalEvent,
|
||||
LLMConfig,
|
||||
} from '@page-agent/core'
|
||||
|
||||
// 等待插件注入(最多 1 秒)
|
||||
@@ -48,18 +47,13 @@ async function waitForExtension(timeout = 1000): Promise<boolean> {
|
||||
|
||||
// 使用
|
||||
if (await waitForExtension()) {
|
||||
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||
'点击登录按钮',
|
||||
{
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: 'your-api-key',
|
||||
model: 'gpt-5.2',
|
||||
},
|
||||
{
|
||||
onStatusChange: (status) => console.log('状态:', status),
|
||||
onActivity: (activity) => console.log('活动:', activity),
|
||||
}
|
||||
)
|
||||
const result = await window.PAGE_AGENT_EXT!.execute('点击登录按钮', {
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: 'your-api-key',
|
||||
model: 'gpt-5.2',
|
||||
onStatusChange: (status) => console.log('状态:', status),
|
||||
onActivity: (activity) => console.log('活动:', activity),
|
||||
})
|
||||
console.log('结果:', result)
|
||||
}
|
||||
```
|
||||
@@ -76,7 +70,7 @@ if (await waitForExtension()) {
|
||||
|
||||
主 API 命名空间对象,包含:
|
||||
|
||||
#### `PAGE_AGENT_EXT.execute(task, llmConfig, hooks?)`
|
||||
#### `PAGE_AGENT_EXT.execute(task, config)`
|
||||
|
||||
执行 Agent 任务。
|
||||
|
||||
@@ -85,8 +79,7 @@ if (await waitForExtension()) {
|
||||
| 名称 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `task` | `string` | 是 | 任务描述 |
|
||||
| `llmConfig` | `LLMConfig` | 是 | LLM 配置 |
|
||||
| `hooks` | `ExecuteHooks` | 否 | 事件回调 |
|
||||
| `config` | `ExecuteConfig` | 是 | 执行配置(LLM 设置、选项和事件回调) |
|
||||
|
||||
**返回:** `Promise<ExecutionResult>`
|
||||
|
||||
@@ -104,21 +97,26 @@ import type {
|
||||
AgentStatus,
|
||||
ExecutionResult,
|
||||
HistoricalEvent,
|
||||
LLMConfig,
|
||||
} from '@page-agent/core'
|
||||
|
||||
export interface ExecuteHooks {
|
||||
export interface ExecuteConfig {
|
||||
baseURL: string
|
||||
apiKey: string
|
||||
model: string
|
||||
|
||||
/**
|
||||
* 是否将初始标签页(运行此脚本的页面)包含在任务中。
|
||||
* @default true
|
||||
*/
|
||||
includeInitialTab?: boolean
|
||||
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
onActivity?: (activity: AgentActivity) => void
|
||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||
onDispose?: () => void
|
||||
}
|
||||
|
||||
export type Execute = (
|
||||
task: string,
|
||||
llmConfig: LLMConfig,
|
||||
hooks?: ExecuteHooks
|
||||
) => Promise<ExecutionResult>
|
||||
export type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||
```
|
||||
|
||||
### AgentStatus
|
||||
@@ -164,16 +162,6 @@ type HistoricalEvent =
|
||||
| { type: 'error'; message: string; rawResponse?: unknown }
|
||||
```
|
||||
|
||||
### LLMConfig
|
||||
|
||||
```typescript
|
||||
interface LLMConfig {
|
||||
baseURL: string // 例如 'https://api.openai.com/v1'
|
||||
apiKey: string
|
||||
model: string // 例如 'gpt-5.2'
|
||||
}
|
||||
```
|
||||
|
||||
### ExecutionResult
|
||||
|
||||
```typescript
|
||||
@@ -205,44 +193,63 @@ if (result.success) {
|
||||
}
|
||||
```
|
||||
|
||||
### 排除初始标签页
|
||||
|
||||
默认情况下,Agent 会将初始标签页(运行脚本的页面)包含在任务中。设置 `includeInitialTab: false` 可以排除它:
|
||||
|
||||
```typescript
|
||||
const result = await window.PAGE_AGENT_EXT!.execute(
|
||||
'打开新标签页并在 GitHub 上搜索 page-agent',
|
||||
{
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
model: 'gpt-5.2',
|
||||
includeInitialTab: false, // Agent 只会打开新标签页
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### 使用事件回调
|
||||
|
||||
```typescript
|
||||
await window.PAGE_AGENT_EXT!.execute(
|
||||
'导航到设置页面',
|
||||
llmConfig,
|
||||
{
|
||||
onStatusChange: (status) => {
|
||||
updateUI({ agentStatus: status })
|
||||
},
|
||||
onActivity: (activity) => {
|
||||
switch (activity.type) {
|
||||
case 'thinking':
|
||||
showSpinner('Agent 正在思考...')
|
||||
break
|
||||
case 'executing':
|
||||
showSpinner(`正在执行: ${activity.tool}`)
|
||||
break
|
||||
case 'executed':
|
||||
log(`${activity.tool} 完成,耗时 ${activity.duration}ms`)
|
||||
break
|
||||
case 'error':
|
||||
showError(activity.message)
|
||||
break
|
||||
}
|
||||
},
|
||||
onHistoryUpdate: (history) => {
|
||||
renderHistory(history)
|
||||
},
|
||||
}
|
||||
)
|
||||
await window.PAGE_AGENT_EXT!.execute('导航到设置页面', {
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
model: 'gpt-5.2',
|
||||
onStatusChange: (status) => {
|
||||
updateUI({ agentStatus: status })
|
||||
},
|
||||
onActivity: (activity) => {
|
||||
switch (activity.type) {
|
||||
case 'thinking':
|
||||
showSpinner('Agent 正在思考...')
|
||||
break
|
||||
case 'executing':
|
||||
showSpinner(`正在执行: ${activity.tool}`)
|
||||
break
|
||||
case 'executed':
|
||||
log(`${activity.tool} 完成,耗时 ${activity.duration}ms`)
|
||||
break
|
||||
case 'error':
|
||||
showError(activity.message)
|
||||
break
|
||||
}
|
||||
},
|
||||
onHistoryUpdate: (history) => {
|
||||
renderHistory(history)
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### 停止执行
|
||||
|
||||
```typescript
|
||||
// 启动任务
|
||||
window.PAGE_AGENT_EXT!.execute('滚动浏览所有页面', llmConfig)
|
||||
window.PAGE_AGENT_EXT!.execute('滚动浏览所有页面', {
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
model: 'gpt-5.2',
|
||||
})
|
||||
|
||||
// 稍后停止
|
||||
window.PAGE_AGENT_EXT!.dispose()
|
||||
@@ -258,24 +265,25 @@ import type {
|
||||
AgentStatus,
|
||||
ExecutionResult,
|
||||
HistoricalEvent,
|
||||
LLMConfig,
|
||||
} from '@page-agent/core'
|
||||
|
||||
interface ExecuteConfig {
|
||||
baseURL: string
|
||||
apiKey: string
|
||||
model: string
|
||||
includeInitialTab?: boolean
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
onActivity?: (activity: AgentActivity) => void
|
||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||
onDispose?: () => void
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
PAGE_AGENT_EXT_VERSION?: string
|
||||
PAGE_AGENT_EXT?: {
|
||||
version: string
|
||||
execute: (
|
||||
task: string,
|
||||
llmConfig: LLMConfig,
|
||||
hooks?: {
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
onActivity?: (activity: AgentActivity) => void
|
||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||
onDispose?: () => void
|
||||
}
|
||||
) => Promise<ExecutionResult>
|
||||
execute: (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||
dispose: () => void
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@page-agent/ext",
|
||||
"private": true,
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
@@ -23,10 +23,10 @@
|
||||
"wxt": "^0.20.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@page-agent/core": "1.1.0",
|
||||
"@page-agent/llms": "1.1.0",
|
||||
"@page-agent/page-controller": "1.1.0",
|
||||
"@page-agent/ui": "1.1.0",
|
||||
"@page-agent/core": "1.1.1",
|
||||
"@page-agent/llms": "1.1.1",
|
||||
"@page-agent/page-controller": "1.1.1",
|
||||
"@page-agent/ui": "1.1.1",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
|
||||
@@ -17,7 +17,7 @@ function detectLanguage(): 'en-US' | 'zh-CN' {
|
||||
* - can be used from a side panel or a content script
|
||||
*/
|
||||
export class MultiPageAgent extends PageAgentCore {
|
||||
constructor(config: Omit<PageAgentConfig, 'pageController'>) {
|
||||
constructor(config: Omit<PageAgentConfig, 'pageController'> & { includeInitialTab?: boolean }) {
|
||||
// multi page controller
|
||||
const tabsController = new TabsController()
|
||||
const pageController = new RemotePageController(tabsController)
|
||||
@@ -31,6 +31,9 @@ export class MultiPageAgent extends PageAgentCore {
|
||||
`Default working language: **${targetLanguage}**`
|
||||
)
|
||||
|
||||
// include initial tab for controlling
|
||||
const includeInitialTab = config.includeInitialTab ?? true
|
||||
|
||||
/**
|
||||
* When the agent is in side-panel and user closed the side-panel.
|
||||
* There is no chance for isAgentRunning to be set false.
|
||||
@@ -47,7 +50,7 @@ export class MultiPageAgent extends PageAgentCore {
|
||||
customSystemPrompt: systemPrompt,
|
||||
|
||||
onBeforeTask: async (agent) => {
|
||||
await tabsController.init(agent.task)
|
||||
await tabsController.init(agent.task, includeInitialTab)
|
||||
|
||||
heartBeatInterval = window.setInterval(() => {
|
||||
chrome.storage.local.set({
|
||||
|
||||
@@ -12,7 +12,7 @@ export class TabsController extends EventTarget {
|
||||
private task: string = ''
|
||||
private windowId: number | null = null
|
||||
|
||||
async init(task: string) {
|
||||
async init(task: string, includeInitialTab: boolean = true) {
|
||||
this.task = task
|
||||
this.tabs = []
|
||||
this.currentTabId = null
|
||||
@@ -26,17 +26,19 @@ export class TabsController extends EventTarget {
|
||||
})
|
||||
|
||||
this.initialTabId = result.tabId
|
||||
this.currentTabId = result.tabId
|
||||
|
||||
this.tabs.push({
|
||||
id: result.tabId,
|
||||
isInitial: true,
|
||||
})
|
||||
|
||||
if (!this.initialTabId) {
|
||||
throw new Error('Failed to get initial tab ID')
|
||||
}
|
||||
|
||||
if (includeInitialTab) {
|
||||
this.currentTabId = this.initialTabId
|
||||
this.tabs.push({
|
||||
id: result.tabId,
|
||||
isInitial: true,
|
||||
})
|
||||
}
|
||||
|
||||
await this.updateCurrentTabId(this.currentTabId)
|
||||
|
||||
const tabChangeHandler = (message: any): void => {
|
||||
@@ -230,6 +232,10 @@ export class TabsController extends EventTarget {
|
||||
`| ${tab.id} | ${url} | ${title} | ${this.currentTabId === tab.id ? '✅' : ''} |`
|
||||
)
|
||||
}
|
||||
if (!this.tabs.length) {
|
||||
summaries.push('\nNo tabs available. Open a tab if needed.')
|
||||
}
|
||||
|
||||
return summaries.join('\n')
|
||||
}
|
||||
|
||||
|
||||
@@ -136,12 +136,12 @@ Here are examples of good output patterns. Use them as reference but never copy
|
||||
</examples>
|
||||
|
||||
<output>
|
||||
You must ALWAYS respond with a valid JSON in this exact format:
|
||||
|
||||
{
|
||||
"evaluation_previous_goal": "Concise one-sentence analysis of your last action. Clearly state success, failure, or uncertain.",
|
||||
"memory": "1-3 concise sentences of specific memory of this step and overall progress. You should put here everything that will help you track progress in future steps. Like counting pages visited, items found, etc.",
|
||||
"next_goal": "State the next immediate goal and action to achieve it, in one clear sentence."
|
||||
"action":{"action_name": {// action-specific parameter}}
|
||||
"next_goal": "State the next immediate goal and action to achieve it, in one clear sentence.",
|
||||
"action":{
|
||||
"Action name": {// Action parameters}
|
||||
}
|
||||
}
|
||||
</output>
|
||||
|
||||
@@ -69,11 +69,11 @@ async function exposeAgentToPage() {
|
||||
}
|
||||
|
||||
try {
|
||||
const { task, llmConfig } = payload
|
||||
const { task, config } = payload
|
||||
|
||||
// create when used
|
||||
|
||||
multiPageAgent = new MultiPageAgent(llmConfig)
|
||||
multiPageAgent = new MultiPageAgent(config)
|
||||
|
||||
// events
|
||||
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import type { AgentActivity, AgentStatus, ExecutionResult, HistoricalEvent } from '@page-agent/core'
|
||||
import type { LLMConfig } from '@page-agent/llms'
|
||||
|
||||
export interface ExecuteHooks {
|
||||
export type Execute = (task: string, config: ExecuteConfig) => Promise<ExecutionResult>
|
||||
|
||||
export interface ExecuteConfig {
|
||||
baseURL: string
|
||||
apiKey: string
|
||||
model: string
|
||||
|
||||
/**
|
||||
* Whether to include the initial tab (that holds this main world script) in the task.
|
||||
* @default true
|
||||
*/
|
||||
includeInitialTab?: boolean
|
||||
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
onActivity?: (activity: AgentActivity) => void
|
||||
onHistoryUpdate?: (history: HistoricalEvent[]) => void
|
||||
onDispose?: () => void
|
||||
}
|
||||
|
||||
export type Execute = (
|
||||
task: string,
|
||||
llmConfig: LLMConfig,
|
||||
hooks?: ExecuteHooks
|
||||
) => Promise<ExecutionResult>
|
||||
|
||||
export default defineUnlistedScript(() => {
|
||||
let _lastId = 0
|
||||
function getId() {
|
||||
@@ -21,13 +26,13 @@ export default defineUnlistedScript(() => {
|
||||
return _lastId
|
||||
}
|
||||
|
||||
const execute: Execute = async (task, llmConfig, hooks) => {
|
||||
const execute: Execute = async (task, config) => {
|
||||
if (typeof task !== 'string') throw new Error('Task must be a string')
|
||||
if (task.trim().length === 0) throw new Error('Task cannot be empty')
|
||||
if (!llmConfig) throw new Error('LLM config is required')
|
||||
if (!llmConfig.baseURL) throw new Error('LLM config must have a baseURL')
|
||||
if (!llmConfig.apiKey) throw new Error('LLM config must have an apiKey')
|
||||
if (!llmConfig.model) throw new Error('LLM config must have a model')
|
||||
if (!config) throw new Error('Config is required')
|
||||
if (!config.baseURL) throw new Error('Config must have a baseURL')
|
||||
if (!config.apiKey) throw new Error('Config must have an apiKey')
|
||||
if (!config.model) throw new Error('Config must have a model')
|
||||
|
||||
const id = getId()
|
||||
|
||||
@@ -40,30 +45,31 @@ export default defineUnlistedScript(() => {
|
||||
|
||||
// events
|
||||
|
||||
if (data.action === 'status_change_event' && hooks?.onStatusChange) {
|
||||
hooks.onStatusChange(data.payload)
|
||||
if (data.action === 'status_change_event' && config.onStatusChange) {
|
||||
config.onStatusChange(data.payload)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.action === 'activity_event' && hooks?.onActivity) {
|
||||
hooks.onActivity(data.payload)
|
||||
if (data.action === 'activity_event' && config.onActivity) {
|
||||
config.onActivity(data.payload)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.action === 'history_change_event' && hooks?.onHistoryUpdate) {
|
||||
hooks.onHistoryUpdate(data.payload)
|
||||
if (data.action === 'history_change_event' && config.onHistoryUpdate) {
|
||||
config.onHistoryUpdate(data.payload)
|
||||
return
|
||||
}
|
||||
|
||||
if (data.action === 'dispose_event' && hooks?.onDispose) {
|
||||
hooks.onDispose()
|
||||
if (data.action === 'dispose_event' && config.onDispose) {
|
||||
config.onDispose()
|
||||
window.removeEventListener('message', handleMessage)
|
||||
return
|
||||
}
|
||||
|
||||
// result
|
||||
|
||||
if (data.action !== 'execute_result') return
|
||||
|
||||
// execute_result
|
||||
|
||||
window.removeEventListener('message', handleMessage)
|
||||
|
||||
if (data.error) {
|
||||
@@ -73,6 +79,7 @@ export default defineUnlistedScript(() => {
|
||||
}
|
||||
}
|
||||
|
||||
// @note will be removed on dispose or result
|
||||
window.addEventListener('message', handleMessage)
|
||||
})
|
||||
|
||||
@@ -81,7 +88,15 @@ export default defineUnlistedScript(() => {
|
||||
channel: 'PAGE_AGENT_EXT_REQUEST',
|
||||
id,
|
||||
action: 'execute',
|
||||
payload: { task, llmConfig },
|
||||
payload: {
|
||||
task,
|
||||
config: {
|
||||
baseURL: config.baseURL,
|
||||
apiKey: config.apiKey,
|
||||
model: config.model,
|
||||
includeInitialTab: config.includeInitialTab,
|
||||
},
|
||||
},
|
||||
},
|
||||
'*'
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/llms",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-agent-llms.js",
|
||||
"module": "./dist/lib/page-agent-llms.js",
|
||||
|
||||
@@ -74,6 +74,10 @@ export function modelPatch(body: Record<string, any>) {
|
||||
} else if (modelName.startsWith('gpt-51')) {
|
||||
debug('Applying GPT-51 patch: disable reasoning')
|
||||
body.reasoning_effort = 'none'
|
||||
} else if (modelName.startsWith('gpt-5-mini')) {
|
||||
debug('Applying GPT-5-mini patch: set reasoning effort to low, temperature to 1')
|
||||
body.reasoning_effort = 'low'
|
||||
body.temperature = 1
|
||||
} else if (modelName.startsWith('gpt-5')) {
|
||||
debug('Applying GPT-5 patch: set reasoning effort to low')
|
||||
body.reasoning_effort = 'low'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "page-agent",
|
||||
"private": false,
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"main": "./dist/esm/page-agent.js",
|
||||
"module": "./dist/esm/page-agent.js",
|
||||
@@ -46,9 +46,9 @@
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"zod": "^4.3.5",
|
||||
"@page-agent/llms": "1.1.0",
|
||||
"@page-agent/page-controller": "1.1.0",
|
||||
"@page-agent/core": "1.1.0",
|
||||
"@page-agent/ui": "1.1.0"
|
||||
"@page-agent/llms": "1.1.1",
|
||||
"@page-agent/page-controller": "1.1.1",
|
||||
"@page-agent/core": "1.1.1",
|
||||
"@page-agent/ui": "1.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/page-controller",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-controller.js",
|
||||
"module": "./dist/lib/page-controller.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@page-agent/ui",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"main": "./dist/lib/page-agent-ui.js",
|
||||
"module": "./dist/lib/page-agent-ui.js",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@page-agent/website",
|
||||
"private": true,
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Demo build (auto-init with demo LLM, for quick testing)
|
||||
export const CDN_DEMO_URL =
|
||||
'https://cdn.jsdelivr.net/npm/page-agent@1.1.0/dist/iife/page-agent.demo.js'
|
||||
'https://cdn.jsdelivr.net/npm/page-agent@1.1.1/dist/iife/page-agent.demo.js'
|
||||
export const CDN_DEMO_CN_URL =
|
||||
'https://registry.npmmirror.com/page-agent/1.1.0/files/dist/iife/page-agent.demo.js'
|
||||
'https://registry.npmmirror.com/page-agent/1.1.1/files/dist/iife/page-agent.demo.js'
|
||||
|
||||
// Demo LLM for website testing
|
||||
export const DEMO_MODEL = 'PAGE-AGENT-FREE-TESTING-RANDOM'
|
||||
|
||||
@@ -152,27 +152,24 @@ localStorage.setItem('PageAgentExtUserAuthToken', '<your-token-from-extension>')
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h3 className="text-xl font-semibold my-3">
|
||||
PAGE_AGENT_EXT.execute(task, llmConfig, hooks?)
|
||||
</h3>
|
||||
<h3 className="text-xl font-semibold my-3">PAGE_AGENT_EXT.execute(task, config)</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
{isZh
|
||||
? '使用 LLM 配置执行任务。返回一个 Promise,在任务完成时 resolve。可选的 hooks 参数用于监听任务执行过程中的事件。'
|
||||
: 'Execute a task with LLM configuration. Returns a Promise that resolves when the task completes. Optional hooks parameter for listening to events during task execution.'}
|
||||
? '使用配置执行任务。返回一个 Promise,在任务完成时 resolve。config 参数包含 LLM 设置、选项和事件回调。'
|
||||
: 'Execute a task with configuration. Returns a Promise that resolves when the task completes. Config includes LLM settings, options, and event callbacks.'}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
code={
|
||||
isZh
|
||||
? `// 使用 LLM 配置和 hooks 执行任务
|
||||
? `// 使用配置执行任务
|
||||
const result = await window.PAGE_AGENT_EXT.execute(
|
||||
'在 GitHub 上搜索 "page-agent" 并打开第一个结果',
|
||||
{
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: 'your-api-key',
|
||||
model: 'gpt-5-2'
|
||||
},
|
||||
{
|
||||
model: 'gpt-5-2',
|
||||
// includeInitialTab: false, // 设为 false 排除初始标签页
|
||||
onStatusChange: status => console.log('状态变化:', status),
|
||||
onActivity: activity => console.log('活动:', activity),
|
||||
onHistoryUpdate: history => console.log('历史更新:', history),
|
||||
@@ -181,15 +178,14 @@ const result = await window.PAGE_AGENT_EXT.execute(
|
||||
)
|
||||
|
||||
console.log(result) // 任务执行结果`
|
||||
: `// Execute a task with LLM configuration and hooks
|
||||
: `// Execute a task with configuration
|
||||
const result = await window.PAGE_AGENT_EXT.execute(
|
||||
'Search for "page-agent" on GitHub and open the first result',
|
||||
{
|
||||
baseURL: 'https://api.openai.com/v1',
|
||||
apiKey: 'your-api-key',
|
||||
model: 'gpt-5-2'
|
||||
},
|
||||
{
|
||||
model: 'gpt-5-2',
|
||||
// includeInitialTab: false, // Set to false to exclude initial tab
|
||||
onStatusChange: status => console.log('Status change:', status),
|
||||
onActivity: activity => console.log('Activity:', activity),
|
||||
onHistoryUpdate: history => console.log('History update:', history),
|
||||
@@ -221,41 +217,26 @@ window.PAGE_AGENT_EXT.dispose()`
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* LLM Config */}
|
||||
{/* ExecuteConfig */}
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-4">{isZh ? 'LLM 配置' : 'LLM Configuration'}</h2>
|
||||
|
||||
<CodeEditor
|
||||
code={
|
||||
isZh
|
||||
? `interface LLMConfig {
|
||||
baseURL: string // LLM API 端点
|
||||
apiKey: string // API 密钥
|
||||
model: string // 模型名称
|
||||
}`
|
||||
: `interface LLMConfig {
|
||||
baseURL: string // LLM API endpoint
|
||||
apiKey: string // API key
|
||||
model: string // Model name
|
||||
}`
|
||||
}
|
||||
language="typescript"
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Execute Hooks */}
|
||||
<section>
|
||||
<h2 className="text-2xl font-bold mb-4">{isZh ? 'Execute Hooks' : 'Execute Hooks'}</h2>
|
||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '执行配置' : 'Execute Configuration'}</h2>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
{isZh
|
||||
? '通过 hooks 参数,你可以监听任务执行过程中的各种事件,实现实时更新 UI、日志记录等功能。'
|
||||
: 'With hooks parameter, you can listen to various events during task execution for real-time UI updates, logging, and more.'}
|
||||
? 'config 参数包含 LLM 设置、选项和事件回调,用于控制任务执行行为。'
|
||||
: 'The config parameter includes LLM settings, options, and event callbacks to control task execution behavior.'}
|
||||
</p>
|
||||
|
||||
<CodeEditor
|
||||
code={
|
||||
isZh
|
||||
? `interface ExecuteHooks {
|
||||
? `interface ExecuteConfig {
|
||||
baseURL: string // LLM API 端点
|
||||
apiKey: string // API 密钥
|
||||
model: string // 模型名称
|
||||
|
||||
// 是否将初始标签页包含在任务中,默认 true
|
||||
includeInitialTab?: boolean
|
||||
|
||||
// Agent 状态变化时调用(idle, running, error, completed 等)
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
|
||||
@@ -268,7 +249,14 @@ window.PAGE_AGENT_EXT.dispose()`
|
||||
// Agent 被停止时调用
|
||||
onDispose?: () => void
|
||||
}`
|
||||
: `interface ExecuteHooks {
|
||||
: `interface ExecuteConfig {
|
||||
baseURL: string // LLM API endpoint
|
||||
apiKey: string // API key
|
||||
model: string // Model name
|
||||
|
||||
// Whether to include the initial tab in the task, default true
|
||||
includeInitialTab?: boolean
|
||||
|
||||
// Called when agent status changes (idle, running, error, completed, etc.)
|
||||
onStatusChange?: (status: AgentStatus) => void
|
||||
|
||||
|
||||
@@ -1,34 +1,49 @@
|
||||
import { Fragment } from 'react'
|
||||
|
||||
import CodeEditor from '@/components/CodeEditor'
|
||||
import { useLanguage } from '@/i18n/context'
|
||||
|
||||
// Recommended models: lightweight with excellent tool call capabilities
|
||||
const MODELS = {
|
||||
recommended: [
|
||||
'gpt-4.1-mini',
|
||||
'claude-haiku-4.5',
|
||||
'gemini-3-flash',
|
||||
'deepseek-3.2',
|
||||
'gpt-5.2',
|
||||
'qwen-3-max',
|
||||
],
|
||||
verified: [
|
||||
'qwen-3-plus',
|
||||
'gpt-4.1',
|
||||
'gpt-5',
|
||||
'gpt-5-mini',
|
||||
'grok-4',
|
||||
'grok-code-fast',
|
||||
'claude-sonnet-3.5',
|
||||
'claude-sonnet-4.5',
|
||||
const BASELINE = new Set([
|
||||
'gpt-5.1',
|
||||
'claude-haiku-4.5',
|
||||
'gemini-3-flash',
|
||||
'deepseek-3.2',
|
||||
'qwen3-coder-next',
|
||||
])
|
||||
|
||||
// Models grouped by brand, newest first
|
||||
const MODEL_GROUPS: Record<string, string[]> = {
|
||||
OpenAI: ['gpt-5.2', 'gpt-5.1', 'gpt-5', 'gpt-5-mini', 'gpt-4.1', 'gpt-4.1-mini'],
|
||||
Google: ['gemini-3-pro', 'gemini-3-flash', 'gemini-2.5'],
|
||||
Qwen: ['qwen3-coder-next', 'qwen-3-max', 'qwen-3-plus', 'qwen3:14b (ollama)'],
|
||||
DeepSeek: ['deepseek-3.2'],
|
||||
Anthropic: [
|
||||
'claude-opus-4.6',
|
||||
'claude-opus-4.5',
|
||||
'gemini-2.5',
|
||||
'gemini-3-pro',
|
||||
'claude-sonnet-4.5',
|
||||
'claude-haiku-4.5',
|
||||
'claude-sonnet-3.5',
|
||||
],
|
||||
xAI: ['grok-4.1-fast', 'grok-4', 'grok-code-fast'],
|
||||
MoonshotAI: ['kimi-k2.5'],
|
||||
'Z.AI': ['glm-4.7'],
|
||||
}
|
||||
|
||||
const ModelBadge = ({ model, baseline }: { model: string; baseline?: boolean }) => (
|
||||
<div
|
||||
className={`px-3 py-1.5 rounded-md text-xs font-medium font-mono transition-colors ${
|
||||
baseline
|
||||
? 'bg-emerald-500 text-white shadow-sm'
|
||||
: 'bg-white/80 dark:bg-gray-800/80 text-gray-800 dark:text-gray-200 border border-gray-300 dark:border-gray-600'
|
||||
}`}
|
||||
>
|
||||
{model}
|
||||
{baseline && <span className="ml-1">⭐</span>}
|
||||
</div>
|
||||
)
|
||||
|
||||
export default function Models() {
|
||||
const { isZh } = useLanguage()
|
||||
const allModels = [...MODELS.recommended, ...MODELS.verified]
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl">
|
||||
@@ -45,28 +60,23 @@ export default function Models() {
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
{isZh
|
||||
? '推荐使用 ToolCall 能力强的轻量级模型。'
|
||||
: 'Recommended: Lightweight models with strong ToolCall capabilities.'}
|
||||
: 'Recommended: Fast, lightweight models with strong ToolCall capabilities.'}
|
||||
</p>
|
||||
<div className="bg-linear-to-br from-emerald-50 to-cyan-50 dark:from-emerald-950/30 dark:to-cyan-950/30 rounded-xl p-6 border border-emerald-200/50 dark:border-emerald-800/50">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{allModels.map((model) => {
|
||||
const isRecommended = MODELS.recommended.includes(model)
|
||||
return (
|
||||
<div
|
||||
key={model}
|
||||
className={`px-3 py-1.5 rounded-md text-sm font-medium font-mono transition-colors ${
|
||||
isRecommended
|
||||
? 'bg-emerald-500 text-white shadow-sm'
|
||||
: 'bg-white/80 dark:bg-gray-800/80 text-gray-800 dark:text-gray-200 border border-gray-300 dark:border-gray-600'
|
||||
}`}
|
||||
>
|
||||
{model}
|
||||
{isRecommended && <span className="ml-1">⭐</span>}
|
||||
<div className="grid grid-cols-[5rem_1fr] gap-x-3 gap-y-3 items-start">
|
||||
{Object.entries(MODEL_GROUPS).map(([brand, models]) => (
|
||||
<Fragment key={brand}>
|
||||
<span className="text-xs font-semibold text-gray-500 dark:text-gray-400 pt-2">
|
||||
{brand}
|
||||
</span>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{models.map((model) => (
|
||||
<ModelBadge key={model} model={model} baseline={BASELINE.has(model)} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 mt-5">⭐ baseline models</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user