mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-09 23:10:20 +08:00
Merge remote-tracking branch 'origin/main' into feat/agents-new
# Conflicts: # package.json # src/renderer/src/aiCore/chunk/AiSdkToChunkAdapter.ts
This commit is contained in:
commit
d4c6131fa3
2
.github/workflows/auto-i18n.yml
vendored
2
.github/workflows/auto-i18n.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.head.ref }}
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
|
|
||||||
- name: 📦 Setting Node.js
|
- name: 📦 Setting Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/claude-code-review.yml
vendored
2
.github/workflows/claude-code-review.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/claude-translator.yml
vendored
2
.github/workflows/claude-translator.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/claude.yml
vendored
2
.github/workflows/claude.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
|||||||
actions: read # Required for Claude to read CI results on PRs
|
actions: read # Required for Claude to read CI results on PRs
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/delete-branch.yml
vendored
2
.github/workflows/delete-branch.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
if: github.event.pull_request.merged == true && github.event.pull_request.head.repo.full_name == github.repository
|
if: github.event.pull_request.merged == true && github.event.pull_request.head.repo.full_name == github.repository
|
||||||
steps:
|
steps:
|
||||||
- name: Delete merged branch
|
- name: Delete merged branch
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.git.deleteRef({
|
github.rest.git.deleteRef({
|
||||||
|
|||||||
2
.github/workflows/nightly-build.yml
vendored
2
.github/workflows/nightly-build.yml
vendored
@ -56,7 +56,7 @@ jobs:
|
|||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/pr-ci.yml
vendored
2
.github/workflows/pr-ci.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -47,7 +47,7 @@ jobs:
|
|||||||
npm version "$VERSION" --no-git-tag-version --allow-same-version
|
npm version "$VERSION" --no-git-tag-version --allow-same-version
|
||||||
|
|
||||||
- name: Install Node.js
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
|
|||||||
12
package.json
12
package.json
@ -99,10 +99,10 @@
|
|||||||
"@agentic/exa": "^7.3.3",
|
"@agentic/exa": "^7.3.3",
|
||||||
"@agentic/searxng": "^7.3.3",
|
"@agentic/searxng": "^7.3.3",
|
||||||
"@agentic/tavily": "^7.3.3",
|
"@agentic/tavily": "^7.3.3",
|
||||||
"@ai-sdk/amazon-bedrock": "^3.0.21",
|
"@ai-sdk/amazon-bedrock": "^3.0.29",
|
||||||
"@ai-sdk/google-vertex": "^3.0.27",
|
"@ai-sdk/google-vertex": "^3.0.33",
|
||||||
"@ai-sdk/mistral": "^2.0.14",
|
"@ai-sdk/mistral": "^2.0.17",
|
||||||
"@ai-sdk/perplexity": "^2.0.9",
|
"@ai-sdk/perplexity": "^2.0.11",
|
||||||
"@ant-design/v5-patch-for-react-19": "^1.0.3",
|
"@ant-design/v5-patch-for-react-19": "^1.0.3",
|
||||||
"@anthropic-ai/sdk": "^0.41.0",
|
"@anthropic-ai/sdk": "^0.41.0",
|
||||||
"@anthropic-ai/vertex-sdk": "patch:@anthropic-ai/vertex-sdk@npm%3A0.11.4#~/.yarn/patches/@anthropic-ai-vertex-sdk-npm-0.11.4-c19cb41edb.patch",
|
"@anthropic-ai/vertex-sdk": "patch:@anthropic-ai/vertex-sdk@npm%3A0.11.4#~/.yarn/patches/@anthropic-ai-vertex-sdk-npm-0.11.4-c19cb41edb.patch",
|
||||||
@ -219,7 +219,7 @@
|
|||||||
"@viz-js/lang-dot": "^1.0.5",
|
"@viz-js/lang-dot": "^1.0.5",
|
||||||
"@viz-js/viz": "^3.14.0",
|
"@viz-js/viz": "^3.14.0",
|
||||||
"@xyflow/react": "^12.4.4",
|
"@xyflow/react": "^12.4.4",
|
||||||
"ai": "^5.0.44",
|
"ai": "^5.0.59",
|
||||||
"antd": "patch:antd@npm%3A5.27.0#~/.yarn/patches/antd-npm-5.27.0-aa91c36546.patch",
|
"antd": "patch:antd@npm%3A5.27.0#~/.yarn/patches/antd-npm-5.27.0-aa91c36546.patch",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"async-mutex": "^0.5.0",
|
"async-mutex": "^0.5.0",
|
||||||
@ -244,7 +244,7 @@
|
|||||||
"dotenv-cli": "^7.4.2",
|
"dotenv-cli": "^7.4.2",
|
||||||
"drizzle-kit": "^0.31.4",
|
"drizzle-kit": "^0.31.4",
|
||||||
"drizzle-orm": "^0.44.5",
|
"drizzle-orm": "^0.44.5",
|
||||||
"electron": "37.4.0",
|
"electron": "37.6.0",
|
||||||
"electron-builder": "26.0.15",
|
"electron-builder": "26.0.15",
|
||||||
"electron-devtools-installer": "^3.2.0",
|
"electron-devtools-installer": "^3.2.0",
|
||||||
"electron-reload": "^2.0.0-alpha.1",
|
"electron-reload": "^2.0.0-alpha.1",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@cherrystudio/ai-core",
|
"name": "@cherrystudio/ai-core",
|
||||||
"version": "1.0.0-alpha.18",
|
"version": "1.0.1",
|
||||||
"description": "Cherry Studio AI Core - Unified AI Provider Interface Based on Vercel AI SDK",
|
"description": "Cherry Studio AI Core - Unified AI Provider Interface Based on Vercel AI SDK",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.mjs",
|
"module": "dist/index.mjs",
|
||||||
@ -36,14 +36,14 @@
|
|||||||
"ai": "^5.0.26"
|
"ai": "^5.0.26"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "^2.0.17",
|
"@ai-sdk/anthropic": "^2.0.22",
|
||||||
"@ai-sdk/azure": "^2.0.30",
|
"@ai-sdk/azure": "^2.0.42",
|
||||||
"@ai-sdk/deepseek": "^1.0.17",
|
"@ai-sdk/deepseek": "^1.0.20",
|
||||||
"@ai-sdk/openai": "^2.0.30",
|
"@ai-sdk/openai": "^2.0.42",
|
||||||
"@ai-sdk/openai-compatible": "^1.0.17",
|
"@ai-sdk/openai-compatible": "^1.0.19",
|
||||||
"@ai-sdk/provider": "^2.0.0",
|
"@ai-sdk/provider": "^2.0.0",
|
||||||
"@ai-sdk/provider-utils": "^3.0.9",
|
"@ai-sdk/provider-utils": "^3.0.10",
|
||||||
"@ai-sdk/xai": "^2.0.18",
|
"@ai-sdk/xai": "^2.0.23",
|
||||||
"zod": "^4.1.5"
|
"zod": "^4.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -217,7 +217,8 @@ export enum codeTools {
|
|||||||
claudeCode = 'claude-code',
|
claudeCode = 'claude-code',
|
||||||
geminiCli = 'gemini-cli',
|
geminiCli = 'gemini-cli',
|
||||||
openaiCodex = 'openai-codex',
|
openaiCodex = 'openai-codex',
|
||||||
iFlowCli = 'iflow-cli'
|
iFlowCli = 'iflow-cli',
|
||||||
|
githubCopilotCli = 'github-copilot-cli'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum terminalApps {
|
export enum terminalApps {
|
||||||
|
|||||||
@ -31,7 +31,10 @@ interface VersionInfo {
|
|||||||
|
|
||||||
class CodeToolsService {
|
class CodeToolsService {
|
||||||
private versionCache: Map<string, { version: string; timestamp: number }> = new Map()
|
private versionCache: Map<string, { version: string; timestamp: number }> = new Map()
|
||||||
private terminalsCache: { terminals: TerminalConfig[]; timestamp: number } | null = null
|
private terminalsCache: {
|
||||||
|
terminals: TerminalConfig[]
|
||||||
|
timestamp: number
|
||||||
|
} | null = null
|
||||||
private customTerminalPaths: Map<string, string> = new Map() // Store user-configured terminal paths
|
private customTerminalPaths: Map<string, string> = new Map() // Store user-configured terminal paths
|
||||||
private readonly CACHE_DURATION = 1000 * 60 * 30 // 30 minutes cache
|
private readonly CACHE_DURATION = 1000 * 60 * 30 // 30 minutes cache
|
||||||
private readonly TERMINALS_CACHE_DURATION = 1000 * 60 * 5 // 5 minutes cache for terminals
|
private readonly TERMINALS_CACHE_DURATION = 1000 * 60 * 5 // 5 minutes cache for terminals
|
||||||
@ -82,6 +85,8 @@ class CodeToolsService {
|
|||||||
return '@qwen-code/qwen-code'
|
return '@qwen-code/qwen-code'
|
||||||
case codeTools.iFlowCli:
|
case codeTools.iFlowCli:
|
||||||
return '@iflow-ai/iflow-cli'
|
return '@iflow-ai/iflow-cli'
|
||||||
|
case codeTools.githubCopilotCli:
|
||||||
|
return '@github/copilot'
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported CLI tool: ${cliTool}`)
|
throw new Error(`Unsupported CLI tool: ${cliTool}`)
|
||||||
}
|
}
|
||||||
@ -99,6 +104,8 @@ class CodeToolsService {
|
|||||||
return 'qwen'
|
return 'qwen'
|
||||||
case codeTools.iFlowCli:
|
case codeTools.iFlowCli:
|
||||||
return 'iflow'
|
return 'iflow'
|
||||||
|
case codeTools.githubCopilotCli:
|
||||||
|
return 'copilot'
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported CLI tool: ${cliTool}`)
|
throw new Error(`Unsupported CLI tool: ${cliTool}`)
|
||||||
}
|
}
|
||||||
@ -144,7 +151,9 @@ class CodeToolsService {
|
|||||||
case terminalApps.powershell:
|
case terminalApps.powershell:
|
||||||
// Check for PowerShell in PATH
|
// Check for PowerShell in PATH
|
||||||
try {
|
try {
|
||||||
await execAsync('powershell -Command "Get-Host"', { timeout: 3000 })
|
await execAsync('powershell -Command "Get-Host"', {
|
||||||
|
timeout: 3000
|
||||||
|
})
|
||||||
return terminal
|
return terminal
|
||||||
} catch {
|
} catch {
|
||||||
try {
|
try {
|
||||||
@ -384,7 +393,9 @@ class CodeToolsService {
|
|||||||
const binDir = path.join(os.homedir(), '.cherrystudio', 'bin')
|
const binDir = path.join(os.homedir(), '.cherrystudio', 'bin')
|
||||||
const executablePath = path.join(binDir, executableName + (isWin ? '.exe' : ''))
|
const executablePath = path.join(binDir, executableName + (isWin ? '.exe' : ''))
|
||||||
|
|
||||||
const { stdout } = await execAsync(`"${executablePath}" --version`, { timeout: 10000 })
|
const { stdout } = await execAsync(`"${executablePath}" --version`, {
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
// Extract version number from output (format may vary by tool)
|
// Extract version number from output (format may vary by tool)
|
||||||
const versionMatch = stdout.trim().match(/\d+\.\d+\.\d+/)
|
const versionMatch = stdout.trim().match(/\d+\.\d+\.\d+/)
|
||||||
installedVersion = versionMatch ? versionMatch[0] : stdout.trim().split(' ')[0]
|
installedVersion = versionMatch ? versionMatch[0] : stdout.trim().split(' ')[0]
|
||||||
@ -425,7 +436,10 @@ class CodeToolsService {
|
|||||||
logger.info(`${packageName} latest version: ${latestVersion}`)
|
logger.info(`${packageName} latest version: ${latestVersion}`)
|
||||||
|
|
||||||
// Cache the result
|
// Cache the result
|
||||||
this.versionCache.set(cacheKey, { version: latestVersion!, timestamp: now })
|
this.versionCache.set(cacheKey, {
|
||||||
|
version: latestVersion!,
|
||||||
|
timestamp: now
|
||||||
|
})
|
||||||
logger.debug(`Cached latest version for ${packageName}`)
|
logger.debug(`Cached latest version for ${packageName}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(`Failed to get latest version for ${packageName}:`, error as Error)
|
logger.warn(`Failed to get latest version for ${packageName}:`, error as Error)
|
||||||
|
|||||||
@ -24,6 +24,8 @@ export class AiSdkToChunkAdapter {
|
|||||||
private isFirstChunk = true
|
private isFirstChunk = true
|
||||||
private enableWebSearch: boolean = false
|
private enableWebSearch: boolean = false
|
||||||
private onSessionUpdate?: (sessionId: string) => void
|
private onSessionUpdate?: (sessionId: string) => void
|
||||||
|
private responseStartTimestamp: number | null = null
|
||||||
|
private firstTokenTimestamp: number | null = null
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private onChunk: (chunk: Chunk) => void,
|
private onChunk: (chunk: Chunk) => void,
|
||||||
@ -38,6 +40,17 @@ export class AiSdkToChunkAdapter {
|
|||||||
this.onSessionUpdate = onSessionUpdate
|
this.onSessionUpdate = onSessionUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private markFirstTokenIfNeeded() {
|
||||||
|
if (this.firstTokenTimestamp === null && this.responseStartTimestamp !== null) {
|
||||||
|
this.firstTokenTimestamp = Date.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetTimingState() {
|
||||||
|
this.responseStartTimestamp = null
|
||||||
|
this.firstTokenTimestamp = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 AI SDK 流结果
|
* 处理 AI SDK 流结果
|
||||||
* @param aiSdkResult AI SDK 的流结果对象
|
* @param aiSdkResult AI SDK 的流结果对象
|
||||||
@ -65,6 +78,8 @@ export class AiSdkToChunkAdapter {
|
|||||||
webSearchResults: [],
|
webSearchResults: [],
|
||||||
reasoningId: ''
|
reasoningId: ''
|
||||||
}
|
}
|
||||||
|
this.resetTimingState()
|
||||||
|
this.responseStartTimestamp = Date.now()
|
||||||
// Reset link converter state at the start of stream
|
// Reset link converter state at the start of stream
|
||||||
this.isFirstChunk = true
|
this.isFirstChunk = true
|
||||||
|
|
||||||
@ -77,6 +92,7 @@ export class AiSdkToChunkAdapter {
|
|||||||
if (this.enableWebSearch) {
|
if (this.enableWebSearch) {
|
||||||
const remainingText = flushLinkConverterBuffer()
|
const remainingText = flushLinkConverterBuffer()
|
||||||
if (remainingText) {
|
if (remainingText) {
|
||||||
|
this.markFirstTokenIfNeeded()
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
type: ChunkType.TEXT_DELTA,
|
type: ChunkType.TEXT_DELTA,
|
||||||
text: remainingText
|
text: remainingText
|
||||||
@ -91,6 +107,7 @@ export class AiSdkToChunkAdapter {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
reader.releaseLock()
|
reader.releaseLock()
|
||||||
|
this.resetTimingState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +169,7 @@ export class AiSdkToChunkAdapter {
|
|||||||
|
|
||||||
// Only emit chunk if there's text to send
|
// Only emit chunk if there's text to send
|
||||||
if (finalText) {
|
if (finalText) {
|
||||||
|
this.markFirstTokenIfNeeded()
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
type: ChunkType.TEXT_DELTA,
|
type: ChunkType.TEXT_DELTA,
|
||||||
text: this.accumulate ? final.text : finalText
|
text: this.accumulate ? final.text : finalText
|
||||||
@ -176,6 +194,9 @@ export class AiSdkToChunkAdapter {
|
|||||||
break
|
break
|
||||||
case 'reasoning-delta':
|
case 'reasoning-delta':
|
||||||
final.reasoningContent += chunk.text || ''
|
final.reasoningContent += chunk.text || ''
|
||||||
|
if (chunk.text) {
|
||||||
|
this.markFirstTokenIfNeeded()
|
||||||
|
}
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
type: ChunkType.THINKING_DELTA,
|
type: ChunkType.THINKING_DELTA,
|
||||||
text: final.reasoningContent || ''
|
text: final.reasoningContent || ''
|
||||||
@ -275,44 +296,37 @@ export class AiSdkToChunkAdapter {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'finish':
|
case 'finish': {
|
||||||
|
const usage = {
|
||||||
|
completion_tokens: chunk.totalUsage?.outputTokens || 0,
|
||||||
|
prompt_tokens: chunk.totalUsage?.inputTokens || 0,
|
||||||
|
total_tokens: chunk.totalUsage?.totalTokens || 0
|
||||||
|
}
|
||||||
|
const metrics = this.buildMetrics(chunk.totalUsage)
|
||||||
|
const baseResponse = {
|
||||||
|
text: final.text || '',
|
||||||
|
reasoning_content: final.reasoningContent || ''
|
||||||
|
}
|
||||||
|
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
type: ChunkType.BLOCK_COMPLETE,
|
type: ChunkType.BLOCK_COMPLETE,
|
||||||
response: {
|
response: {
|
||||||
text: final.text || '',
|
...baseResponse,
|
||||||
reasoning_content: final.reasoningContent || '',
|
usage: { ...usage },
|
||||||
usage: {
|
metrics: metrics ? { ...metrics } : undefined
|
||||||
completion_tokens: chunk.totalUsage.outputTokens || 0,
|
|
||||||
prompt_tokens: chunk.totalUsage.inputTokens || 0,
|
|
||||||
total_tokens: chunk.totalUsage.totalTokens || 0
|
|
||||||
},
|
|
||||||
metrics: chunk.totalUsage
|
|
||||||
? {
|
|
||||||
completion_tokens: chunk.totalUsage.outputTokens || 0,
|
|
||||||
time_completion_millsec: 0
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.onChunk({
|
this.onChunk({
|
||||||
type: ChunkType.LLM_RESPONSE_COMPLETE,
|
type: ChunkType.LLM_RESPONSE_COMPLETE,
|
||||||
response: {
|
response: {
|
||||||
text: final.text || '',
|
...baseResponse,
|
||||||
reasoning_content: final.reasoningContent || '',
|
usage: { ...usage },
|
||||||
usage: {
|
metrics: metrics ? { ...metrics } : undefined
|
||||||
completion_tokens: chunk.totalUsage.outputTokens || 0,
|
|
||||||
prompt_tokens: chunk.totalUsage.inputTokens || 0,
|
|
||||||
total_tokens: chunk.totalUsage.totalTokens || 0
|
|
||||||
},
|
|
||||||
metrics: chunk.totalUsage
|
|
||||||
? {
|
|
||||||
completion_tokens: chunk.totalUsage.outputTokens || 0,
|
|
||||||
time_completion_millsec: 0
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
this.resetTimingState()
|
||||||
break
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// === 源和文件相关事件 ===
|
// === 源和文件相关事件 ===
|
||||||
case 'source':
|
case 'source':
|
||||||
@ -348,6 +362,34 @@ export class AiSdkToChunkAdapter {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private buildMetrics(totalUsage?: {
|
||||||
|
inputTokens?: number | null
|
||||||
|
outputTokens?: number | null
|
||||||
|
totalTokens?: number | null
|
||||||
|
}) {
|
||||||
|
if (!totalUsage) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const completionTokens = totalUsage.outputTokens ?? 0
|
||||||
|
const now = Date.now()
|
||||||
|
const start = this.responseStartTimestamp ?? now
|
||||||
|
const firstToken = this.firstTokenTimestamp
|
||||||
|
const timeFirstToken = Math.max(firstToken != null ? firstToken - start : 0, 0)
|
||||||
|
const baseForCompletion = firstToken ?? start
|
||||||
|
let timeCompletion = Math.max(now - baseForCompletion, 0)
|
||||||
|
|
||||||
|
if (timeCompletion === 0 && completionTokens > 0) {
|
||||||
|
timeCompletion = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
completion_tokens: completionTokens,
|
||||||
|
time_first_token_millsec: timeFirstToken,
|
||||||
|
time_completion_millsec: timeCompletion
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AiSdkToChunkAdapter
|
export default AiSdkToChunkAdapter
|
||||||
|
|||||||
@ -75,10 +75,15 @@ export interface CodeEditorProps {
|
|||||||
/** CSS class name appended to the default `code-editor` class. */
|
/** CSS class name appended to the default `code-editor` class. */
|
||||||
className?: string
|
className?: string
|
||||||
/**
|
/**
|
||||||
* Whether the editor is editable.
|
* Whether the editor view is editable.
|
||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
editable?: boolean
|
editable?: boolean
|
||||||
|
/**
|
||||||
|
* Set the editor state to read only but keep some user interactions, e.g., keymaps.
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readOnly?: boolean
|
||||||
/**
|
/**
|
||||||
* Whether the editor is expanded.
|
* Whether the editor is expanded.
|
||||||
* If true, the height and maxHeight props are ignored.
|
* If true, the height and maxHeight props are ignored.
|
||||||
@ -114,6 +119,7 @@ const CodeEditor = ({
|
|||||||
style,
|
style,
|
||||||
className,
|
className,
|
||||||
editable = true,
|
editable = true,
|
||||||
|
readOnly = false,
|
||||||
expanded = true,
|
expanded = true,
|
||||||
wrapped = true
|
wrapped = true
|
||||||
}: CodeEditorProps) => {
|
}: CodeEditorProps) => {
|
||||||
@ -189,6 +195,7 @@ const CodeEditor = ({
|
|||||||
maxHeight={expanded ? undefined : maxHeight}
|
maxHeight={expanded ? undefined : maxHeight}
|
||||||
minHeight={minHeight}
|
minHeight={minHeight}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
|
readOnly={readOnly}
|
||||||
// @ts-ignore 强制使用,见 react-codemirror 的 Example.tsx
|
// @ts-ignore 强制使用,见 react-codemirror 的 Example.tsx
|
||||||
theme={activeCmTheme}
|
theme={activeCmTheme}
|
||||||
extensions={customExtensions}
|
extensions={customExtensions}
|
||||||
|
|||||||
@ -38,6 +38,7 @@ interface PopupContainerProps {
|
|||||||
message?: Message
|
message?: Message
|
||||||
messages?: Message[]
|
messages?: Message[]
|
||||||
topic?: Topic
|
topic?: Topic
|
||||||
|
rawContent?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换文件信息数组为树形结构
|
// 转换文件信息数组为树形结构
|
||||||
@ -140,7 +141,8 @@ const PopupContainer: React.FC<PopupContainerProps> = ({
|
|||||||
resolve,
|
resolve,
|
||||||
message,
|
message,
|
||||||
messages,
|
messages,
|
||||||
topic
|
topic,
|
||||||
|
rawContent
|
||||||
}) => {
|
}) => {
|
||||||
const defaultObsidianVault = store.getState().settings.defaultObsidianVault
|
const defaultObsidianVault = store.getState().settings.defaultObsidianVault
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
@ -229,7 +231,9 @@ const PopupContainer: React.FC<PopupContainerProps> = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let markdown = ''
|
let markdown = ''
|
||||||
if (topic) {
|
if (rawContent) {
|
||||||
|
markdown = rawContent
|
||||||
|
} else if (topic) {
|
||||||
markdown = await topicToMarkdown(topic, exportReasoning)
|
markdown = await topicToMarkdown(topic, exportReasoning)
|
||||||
} else if (messages && messages.length > 0) {
|
} else if (messages && messages.length > 0) {
|
||||||
markdown = messagesToMarkdown(messages, exportReasoning)
|
markdown = messagesToMarkdown(messages, exportReasoning)
|
||||||
@ -299,7 +303,6 @@ const PopupContainer: React.FC<PopupContainerProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={i18n.t('chat.topics.export.obsidian_atributes')}
|
title={i18n.t('chat.topics.export.obsidian_atributes')}
|
||||||
@ -410,9 +413,11 @@ const PopupContainer: React.FC<PopupContainerProps> = ({
|
|||||||
</Option>
|
</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={i18n.t('chat.topics.export.obsidian_reasoning')}>
|
{!rawContent && (
|
||||||
<Switch checked={exportReasoning} onChange={setExportReasoning} />
|
<Form.Item label={i18n.t('chat.topics.export.obsidian_reasoning')}>
|
||||||
</Form.Item>
|
<Switch checked={exportReasoning} onChange={setExportReasoning} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ interface ObsidianExportOptions {
|
|||||||
topic?: Topic
|
topic?: Topic
|
||||||
message?: Message
|
message?: Message
|
||||||
messages?: Message[]
|
messages?: Message[]
|
||||||
|
rawContent?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ObsidianExportPopup {
|
export default class ObsidianExportPopup {
|
||||||
@ -24,6 +25,7 @@ export default class ObsidianExportPopup {
|
|||||||
topic={options.topic}
|
topic={options.topic}
|
||||||
message={options.message}
|
message={options.message}
|
||||||
messages={options.messages}
|
messages={options.messages}
|
||||||
|
rawContent={options.rawContent}
|
||||||
obsidianTags={''}
|
obsidianTags={''}
|
||||||
open={true}
|
open={true}
|
||||||
resolve={(v) => {
|
resolve={(v) => {
|
||||||
|
|||||||
@ -55,12 +55,15 @@ const PopupContainer: React.FC<Props> = ({ text, title, extension, resolve }) =>
|
|||||||
footer={null}>
|
footer={null}>
|
||||||
{extension !== undefined ? (
|
{extension !== undefined ? (
|
||||||
<Editor
|
<Editor
|
||||||
editable={false}
|
readOnly={true}
|
||||||
expanded={false}
|
expanded={false}
|
||||||
height="100%"
|
height="100%"
|
||||||
style={{ height: '100%' }}
|
style={{ height: '100%' }}
|
||||||
value={text}
|
value={text}
|
||||||
language={extension}
|
language={extension}
|
||||||
|
options={{
|
||||||
|
keymap: true
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text>{text}</Text>
|
<Text>{text}</Text>
|
||||||
|
|||||||
@ -237,7 +237,17 @@ const TabsContainer: React.FC<TabsContainerProps> = ({ children }) => {
|
|||||||
onSortEnd={onSortEnd}
|
onSortEnd={onSortEnd}
|
||||||
className="tabs-sortable"
|
className="tabs-sortable"
|
||||||
renderItem={(tab) => (
|
renderItem={(tab) => (
|
||||||
<Tab key={tab.id} active={tab.id === activeTabId} onClick={() => handleTabClick(tab)}>
|
<Tab
|
||||||
|
key={tab.id}
|
||||||
|
active={tab.id === activeTabId}
|
||||||
|
onClick={() => handleTabClick(tab)}
|
||||||
|
onAuxClick={(e) => {
|
||||||
|
if (e.button === 1 && tab.id !== 'home') {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
closeTab(tab.id)
|
||||||
|
}
|
||||||
|
}}>
|
||||||
<TabHeader>
|
<TabHeader>
|
||||||
{tab.id && <TabIcon>{getTabIcon(tab.id, minapps, minAppsCache)}</TabIcon>}
|
{tab.id && <TabIcon>{getTabIcon(tab.id, minapps, minAppsCache)}</TabIcon>}
|
||||||
<TabTitle>{getTabTitle(tab.id)}</TabTitle>
|
<TabTitle>{getTabTitle(tab.id)}</TabTitle>
|
||||||
|
|||||||
@ -430,6 +430,12 @@ export const SYSTEM_MODELS: Record<SystemProviderId | 'defaultModel', Model[]> =
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
anthropic: [
|
anthropic: [
|
||||||
|
{
|
||||||
|
id: 'claude-sonnet-4-5-20250929',
|
||||||
|
provider: 'anthropic',
|
||||||
|
name: 'Claude Sonnet 4.5',
|
||||||
|
group: 'Claude 4.5'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'claude-sonnet-4-20250514',
|
id: 'claude-sonnet-4-20250514',
|
||||||
provider: 'anthropic',
|
provider: 'anthropic',
|
||||||
@ -698,6 +704,12 @@ export const SYSTEM_MODELS: Record<SystemProviderId | 'defaultModel', Model[]> =
|
|||||||
name: 'GLM-4.5-Flash',
|
name: 'GLM-4.5-Flash',
|
||||||
group: 'GLM-4.5'
|
group: 'GLM-4.5'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'glm-4.6',
|
||||||
|
provider: 'zhipu',
|
||||||
|
name: 'GLM-4.6',
|
||||||
|
group: 'GLM-4.6'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'glm-4.5',
|
id: 'glm-4.5',
|
||||||
provider: 'zhipu',
|
provider: 'zhipu',
|
||||||
|
|||||||
@ -178,9 +178,13 @@ export function isGeminiReasoningModel(model?: Model): boolean {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gemini 支持思考模式的模型正则
|
||||||
|
export const GEMINI_THINKING_MODEL_REGEX =
|
||||||
|
/gemini-(?:2\.5.*(?:-latest)?|flash-latest|pro-latest|flash-lite-latest)(?:-[\w-]+)*$/i
|
||||||
|
|
||||||
export const isSupportedThinkingTokenGeminiModel = (model: Model): boolean => {
|
export const isSupportedThinkingTokenGeminiModel = (model: Model): boolean => {
|
||||||
const modelId = getLowerBaseModelName(model.id, '/')
|
const modelId = getLowerBaseModelName(model.id, '/')
|
||||||
if (modelId.includes('gemini-2.5')) {
|
if (GEMINI_THINKING_MODEL_REGEX.test(modelId)) {
|
||||||
if (modelId.includes('image') || modelId.includes('tts')) {
|
if (modelId.includes('image') || modelId.includes('tts')) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -335,14 +339,20 @@ export const isSupportedReasoningEffortPerplexityModel = (model: Model): boolean
|
|||||||
|
|
||||||
export const isSupportedThinkingTokenZhipuModel = (model: Model): boolean => {
|
export const isSupportedThinkingTokenZhipuModel = (model: Model): boolean => {
|
||||||
const modelId = getLowerBaseModelName(model.id, '/')
|
const modelId = getLowerBaseModelName(model.id, '/')
|
||||||
return modelId.includes('glm-4.5')
|
return ['glm-4.5', 'glm-4.6'].some((id) => modelId.includes(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isDeepSeekHybridInferenceModel = (model: Model) => {
|
export const isDeepSeekHybridInferenceModel = (model: Model) => {
|
||||||
const modelId = getLowerBaseModelName(model.id)
|
const modelId = getLowerBaseModelName(model.id)
|
||||||
// deepseek官方使用chat和reasoner做推理控制,其他provider需要单独判断,id可能会有所差别
|
// deepseek官方使用chat和reasoner做推理控制,其他provider需要单独判断,id可能会有所差别
|
||||||
// openrouter: deepseek/deepseek-chat-v3.1 不知道会不会有其他provider仿照ds官方分出一个同id的作为非思考模式的模型,这里有风险
|
// openrouter: deepseek/deepseek-chat-v3.1 不知道会不会有其他provider仿照ds官方分出一个同id的作为非思考模式的模型,这里有风险
|
||||||
return /deepseek-v3(?:\.1|-1-\d+)/.test(modelId) || modelId.includes('deepseek-chat-v3.1')
|
// Matches: "deepseek-v3" followed by ".digit" or "-digit".
|
||||||
|
// Optionally, this can be followed by ".alphanumeric_sequence" or "-alphanumeric_sequence"
|
||||||
|
// until the end of the string.
|
||||||
|
// Examples: deepseek-v3.1, deepseek-v3-1, deepseek-v3.1.2, deepseek-v3.1-alpha
|
||||||
|
// Does NOT match: deepseek-v3.123 (missing separator after '1'), deepseek-v3.x (x isn't a digit)
|
||||||
|
// TODO: move to utils and add test cases
|
||||||
|
return /deepseek-v3(?:\.\d|-\d)(?:(\.|-)\w+)?$/.test(modelId) || modelId.includes('deepseek-chat-v3.1')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isSupportedThinkingTokenDeepSeekModel = isDeepSeekHybridInferenceModel
|
export const isSupportedThinkingTokenDeepSeekModel = isDeepSeekHybridInferenceModel
|
||||||
|
|||||||
@ -12,6 +12,7 @@ const visionAllowedModels = [
|
|||||||
'gemini-1\\.5',
|
'gemini-1\\.5',
|
||||||
'gemini-2\\.0',
|
'gemini-2\\.0',
|
||||||
'gemini-2\\.5',
|
'gemini-2\\.5',
|
||||||
|
'gemini-(flash|pro|flash-lite)-latest',
|
||||||
'gemini-exp',
|
'gemini-exp',
|
||||||
'claude-3',
|
'claude-3',
|
||||||
'claude-sonnet-4',
|
'claude-sonnet-4',
|
||||||
@ -21,7 +22,9 @@ const visionAllowedModels = [
|
|||||||
'qwen-vl',
|
'qwen-vl',
|
||||||
'qwen2-vl',
|
'qwen2-vl',
|
||||||
'qwen2.5-vl',
|
'qwen2.5-vl',
|
||||||
|
'qwen3-vl',
|
||||||
'qwen2.5-omni',
|
'qwen2.5-omni',
|
||||||
|
'qwen3-omni',
|
||||||
'qvq',
|
'qvq',
|
||||||
'internvl2',
|
'internvl2',
|
||||||
'grok-vision-beta',
|
'grok-vision-beta',
|
||||||
|
|||||||
@ -11,9 +11,12 @@ export const CLAUDE_SUPPORTED_WEBSEARCH_REGEX = new RegExp(
|
|||||||
'i'
|
'i'
|
||||||
)
|
)
|
||||||
|
|
||||||
export const GEMINI_FLASH_MODEL_REGEX = new RegExp('gemini-.*-flash.*$')
|
export const GEMINI_FLASH_MODEL_REGEX = new RegExp('gemini.*-flash.*$')
|
||||||
|
|
||||||
export const GEMINI_SEARCH_REGEX = new RegExp('gemini-2\\..*', 'i')
|
export const GEMINI_SEARCH_REGEX = new RegExp(
|
||||||
|
'gemini-(?:2.*(?:-latest)?|flash-latest|pro-latest|flash-lite-latest)(?:-[\\w-]+)*$',
|
||||||
|
'i'
|
||||||
|
)
|
||||||
|
|
||||||
export const PERPLEXITY_SEARCH_MODELS = [
|
export const PERPLEXITY_SEARCH_MODELS = [
|
||||||
'sonar-pro',
|
'sonar-pro',
|
||||||
|
|||||||
@ -108,7 +108,11 @@ export const useCodeTools = () => {
|
|||||||
const environmentVariables = codeToolsState?.environmentVariables?.[codeToolsState.selectedCliTool] || ''
|
const environmentVariables = codeToolsState?.environmentVariables?.[codeToolsState.selectedCliTool] || ''
|
||||||
|
|
||||||
// 检查是否可以启动(所有必需字段都已填写)
|
// 检查是否可以启动(所有必需字段都已填写)
|
||||||
const canLaunch = Boolean(codeToolsState.selectedCliTool && selectedModel && codeToolsState.currentDirectory)
|
const canLaunch = Boolean(
|
||||||
|
codeToolsState.selectedCliTool &&
|
||||||
|
codeToolsState.currentDirectory &&
|
||||||
|
(codeToolsState.selectedCliTool === codeTools.githubCopilotCli || selectedModel)
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// 状态
|
// 状态
|
||||||
|
|||||||
@ -48,6 +48,17 @@ export function useActiveTopic(assistantId: string, topic?: Topic) {
|
|||||||
}
|
}
|
||||||
}, [activeTopic?.id, assistant])
|
}, [activeTopic?.id, assistant])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!assistant?.topics?.length || !activeTopic) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestTopic = assistant.topics.find((item) => item.id === activeTopic.id)
|
||||||
|
if (latestTopic && latestTopic !== activeTopic) {
|
||||||
|
setActiveTopic(latestTopic)
|
||||||
|
}
|
||||||
|
}, [assistant?.topics, activeTopic])
|
||||||
|
|
||||||
return { activeTopic, setActiveTopic }
|
return { activeTopic, setActiveTopic }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1920,6 +1920,12 @@
|
|||||||
"provider_settings": "Go to provider settings"
|
"provider_settings": "Go to provider settings"
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
|
"auto_rename": {
|
||||||
|
"empty_note": "Note is empty, cannot generate name",
|
||||||
|
"failed": "Failed to generate note name",
|
||||||
|
"label": "Generate Note Name",
|
||||||
|
"success": "Note name generated successfully"
|
||||||
|
},
|
||||||
"characters": "Characters",
|
"characters": "Characters",
|
||||||
"collapse": "Collapse",
|
"collapse": "Collapse",
|
||||||
"content_placeholder": "Please enter the note content...",
|
"content_placeholder": "Please enter the note content...",
|
||||||
|
|||||||
@ -1920,6 +1920,12 @@
|
|||||||
"provider_settings": "跳转到服务商设置界面"
|
"provider_settings": "跳转到服务商设置界面"
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
|
"auto_rename": {
|
||||||
|
"empty_note": "笔记为空,无法生成名称",
|
||||||
|
"failed": "生成笔记名称失败",
|
||||||
|
"label": "生成笔记名称",
|
||||||
|
"success": "笔记名称生成成功"
|
||||||
|
},
|
||||||
"characters": "字符",
|
"characters": "字符",
|
||||||
"collapse": "收起",
|
"collapse": "收起",
|
||||||
"content_placeholder": "请输入笔记内容...",
|
"content_placeholder": "请输入笔记内容...",
|
||||||
|
|||||||
@ -1920,6 +1920,12 @@
|
|||||||
"provider_settings": "跳轉到服務商設置界面"
|
"provider_settings": "跳轉到服務商設置界面"
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
|
"auto_rename": {
|
||||||
|
"empty_note": "筆記為空,無法生成名稱",
|
||||||
|
"failed": "生成筆記名稱失敗",
|
||||||
|
"label": "生成筆記名稱",
|
||||||
|
"success": "筆記名稱生成成功"
|
||||||
|
},
|
||||||
"characters": "字符",
|
"characters": "字符",
|
||||||
"collapse": "收起",
|
"collapse": "收起",
|
||||||
"content_placeholder": "請輸入筆記內容...",
|
"content_placeholder": "請輸入筆記內容...",
|
||||||
|
|||||||
@ -98,6 +98,10 @@ const CodeToolsPage: FC = () => {
|
|||||||
return m.id.includes('openai') || OPENAI_CODEX_SUPPORTED_PROVIDERS.includes(m.provider)
|
return m.id.includes('openai') || OPENAI_CODEX_SUPPORTED_PROVIDERS.includes(m.provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedCliTool === codeTools.githubCopilotCli) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedCliTool === codeTools.qwenCode || selectedCliTool === codeTools.iFlowCli) {
|
if (selectedCliTool === codeTools.qwenCode || selectedCliTool === codeTools.iFlowCli) {
|
||||||
if (m.supported_endpoint_types) {
|
if (m.supported_endpoint_types) {
|
||||||
return ['openai', 'openai-response'].some((type) =>
|
return ['openai', 'openai-response'].some((type) =>
|
||||||
@ -196,7 +200,7 @@ const CodeToolsPage: FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedModel) {
|
if (!selectedModel && selectedCliTool !== codeTools.githubCopilotCli) {
|
||||||
return { isValid: false, message: t('code.model_required') }
|
return { isValid: false, message: t('code.model_required') }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +209,11 @@ const CodeToolsPage: FC = () => {
|
|||||||
|
|
||||||
// 准备启动环境
|
// 准备启动环境
|
||||||
const prepareLaunchEnvironment = async (): Promise<Record<string, string> | null> => {
|
const prepareLaunchEnvironment = async (): Promise<Record<string, string> | null> => {
|
||||||
|
if (selectedCliTool === codeTools.githubCopilotCli) {
|
||||||
|
const userEnv = parseEnvironmentVariables(environmentVariables)
|
||||||
|
return userEnv
|
||||||
|
}
|
||||||
|
|
||||||
if (!selectedModel) return null
|
if (!selectedModel) return null
|
||||||
|
|
||||||
const modelProvider = getProviderByModel(selectedModel)
|
const modelProvider = getProviderByModel(selectedModel)
|
||||||
@ -229,7 +238,9 @@ const CodeToolsPage: FC = () => {
|
|||||||
|
|
||||||
// 执行启动操作
|
// 执行启动操作
|
||||||
const executeLaunch = async (env: Record<string, string>) => {
|
const executeLaunch = async (env: Record<string, string>) => {
|
||||||
window.api.codeTools.run(selectedCliTool, selectedModel?.id!, currentDirectory, env, {
|
const modelId = selectedCliTool === codeTools.githubCopilotCli ? '' : selectedModel?.id!
|
||||||
|
|
||||||
|
window.api.codeTools.run(selectedCliTool, modelId, currentDirectory, env, {
|
||||||
autoUpdateToLatest,
|
autoUpdateToLatest,
|
||||||
terminal: selectedTerminal
|
terminal: selectedTerminal
|
||||||
})
|
})
|
||||||
@ -316,7 +327,12 @@ const CodeToolsPage: FC = () => {
|
|||||||
banner
|
banner
|
||||||
style={{ borderRadius: 'var(--list-item-border-radius)' }}
|
style={{ borderRadius: 'var(--list-item-border-radius)' }}
|
||||||
message={
|
message={
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
<span>{t('code.bun_required_message')}</span>
|
<span>{t('code.bun_required_message')}</span>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
@ -345,46 +361,64 @@ const CodeToolsPage: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
<SettingsItem>
|
{selectedCliTool !== codeTools.githubCopilotCli && (
|
||||||
<div className="settings-label">
|
<SettingsItem>
|
||||||
{t('code.model')}
|
<div className="settings-label">
|
||||||
{selectedCliTool === 'claude-code' && (
|
{t('code.model')}
|
||||||
<Popover
|
{selectedCliTool === 'claude-code' && (
|
||||||
content={
|
<Popover
|
||||||
<div style={{ width: 200 }}>
|
content={
|
||||||
<div style={{ marginBottom: 8, fontWeight: 500 }}>{t('code.supported_providers')}</div>
|
<div style={{ width: 200 }}>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
<div style={{ marginBottom: 8, fontWeight: 500 }}>{t('code.supported_providers')}</div>
|
||||||
{getClaudeSupportedProviders(allProviders).map((provider) => {
|
<div
|
||||||
return (
|
style={{
|
||||||
<Link
|
display: 'flex',
|
||||||
key={provider.id}
|
flexDirection: 'column',
|
||||||
style={{ color: 'var(--color-text)', display: 'flex', alignItems: 'center', gap: 4 }}
|
gap: 8
|
||||||
to={`/settings/provider?id=${provider.id}`}>
|
}}>
|
||||||
<ProviderLogo shape="square" src={getProviderLogo(provider.id)} size={20} />
|
{getClaudeSupportedProviders(allProviders).map((provider) => {
|
||||||
{getProviderLabel(provider.id)}
|
return (
|
||||||
<ArrowUpRight size={14} />
|
<Link
|
||||||
</Link>
|
key={provider.id}
|
||||||
)
|
style={{
|
||||||
})}
|
color: 'var(--color-text)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 4
|
||||||
|
}}
|
||||||
|
to={`/settings/provider?id=${provider.id}`}>
|
||||||
|
<ProviderLogo shape="square" src={getProviderLogo(provider.id)} size={20} />
|
||||||
|
{getProviderLabel(provider.id)}
|
||||||
|
<ArrowUpRight size={14} />
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
trigger="hover"
|
||||||
trigger="hover"
|
placement="right">
|
||||||
placement="right">
|
<HelpCircle
|
||||||
<HelpCircle size={14} style={{ color: 'var(--color-text-3)', cursor: 'pointer' }} />
|
size={14}
|
||||||
</Popover>
|
style={{
|
||||||
)}
|
color: 'var(--color-text-3)',
|
||||||
</div>
|
cursor: 'pointer'
|
||||||
<ModelSelector
|
}}
|
||||||
providers={availableProviders}
|
/>
|
||||||
predicate={modelPredicate}
|
</Popover>
|
||||||
style={{ width: '100%' }}
|
)}
|
||||||
placeholder={t('code.model_placeholder')}
|
</div>
|
||||||
value={selectedModel ? getModelUniqId(selectedModel) : undefined}
|
<ModelSelector
|
||||||
onChange={handleModelChange}
|
providers={availableProviders}
|
||||||
allowClear
|
predicate={modelPredicate}
|
||||||
/>
|
style={{ width: '100%' }}
|
||||||
</SettingsItem>
|
placeholder={t('code.model_placeholder')}
|
||||||
|
value={selectedModel ? getModelUniqId(selectedModel) : undefined}
|
||||||
|
onChange={handleModelChange}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
)}
|
||||||
|
|
||||||
<SettingsItem>
|
<SettingsItem>
|
||||||
<div className="settings-label">{t('code.working_directory')}</div>
|
<div className="settings-label">{t('code.working_directory')}</div>
|
||||||
@ -403,11 +437,27 @@ const CodeToolsPage: FC = () => {
|
|||||||
options={directories.map((dir) => ({
|
options={directories.map((dir) => ({
|
||||||
value: dir,
|
value: dir,
|
||||||
label: (
|
label: (
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
<div
|
||||||
<span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }}>{dir}</span>
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis'
|
||||||
|
}}>
|
||||||
|
{dir}
|
||||||
|
</span>
|
||||||
<X
|
<X
|
||||||
size={14}
|
size={14}
|
||||||
style={{ marginLeft: 8, cursor: 'pointer', color: '#999' }}
|
style={{
|
||||||
|
marginLeft: 8,
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: '#999'
|
||||||
|
}}
|
||||||
onClick={(e) => handleRemoveDirectory(dir, e)}
|
onClick={(e) => handleRemoveDirectory(dir, e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -429,7 +479,14 @@ const CodeToolsPage: FC = () => {
|
|||||||
rows={2}
|
rows={2}
|
||||||
style={{ fontFamily: 'monospace' }}
|
style={{ fontFamily: 'monospace' }}
|
||||||
/>
|
/>
|
||||||
<div style={{ fontSize: 12, color: 'var(--color-text-3)', marginTop: 4 }}>{t('code.env_vars_help')}</div>
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
color: 'var(--color-text-3)',
|
||||||
|
marginTop: 4
|
||||||
|
}}>
|
||||||
|
{t('code.env_vars_help')}
|
||||||
|
</div>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
{/* 终端选择 (macOS 和 Windows) */}
|
{/* 终端选择 (macOS 和 Windows) */}
|
||||||
@ -464,7 +521,12 @@ const CodeToolsPage: FC = () => {
|
|||||||
selectedTerminal !== terminalApps.cmd &&
|
selectedTerminal !== terminalApps.cmd &&
|
||||||
selectedTerminal !== terminalApps.powershell &&
|
selectedTerminal !== terminalApps.powershell &&
|
||||||
selectedTerminal !== terminalApps.windowsTerminal && (
|
selectedTerminal !== terminalApps.windowsTerminal && (
|
||||||
<div style={{ fontSize: 12, color: 'var(--color-text-3)', marginTop: 4 }}>
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 12,
|
||||||
|
color: 'var(--color-text-3)',
|
||||||
|
marginTop: 4
|
||||||
|
}}>
|
||||||
{terminalCustomPaths[selectedTerminal]
|
{terminalCustomPaths[selectedTerminal]
|
||||||
? `${t('code.custom_path')}: ${terminalCustomPaths[selectedTerminal]}`
|
? `${t('code.custom_path')}: ${terminalCustomPaths[selectedTerminal]}`
|
||||||
: t('code.custom_path_required')}
|
: t('code.custom_path_required')}
|
||||||
|
|||||||
@ -20,7 +20,8 @@ export const CLI_TOOLS = [
|
|||||||
{ value: codeTools.qwenCode, label: 'Qwen Code' },
|
{ value: codeTools.qwenCode, label: 'Qwen Code' },
|
||||||
{ value: codeTools.geminiCli, label: 'Gemini CLI' },
|
{ value: codeTools.geminiCli, label: 'Gemini CLI' },
|
||||||
{ value: codeTools.openaiCodex, label: 'OpenAI Codex' },
|
{ value: codeTools.openaiCodex, label: 'OpenAI Codex' },
|
||||||
{ value: codeTools.iFlowCli, label: 'iFlow CLI' }
|
{ value: codeTools.iFlowCli, label: 'iFlow CLI' },
|
||||||
|
{ value: codeTools.githubCopilotCli, label: 'GitHub Copilot CLI' }
|
||||||
]
|
]
|
||||||
|
|
||||||
export const GEMINI_SUPPORTED_PROVIDERS = ['aihubmix', 'dmxapi', 'new-api', 'cherryin']
|
export const GEMINI_SUPPORTED_PROVIDERS = ['aihubmix', 'dmxapi', 'new-api', 'cherryin']
|
||||||
@ -43,7 +44,8 @@ export const CLI_TOOL_PROVIDER_MAP: Record<string, (providers: Provider[]) => Pr
|
|||||||
[codeTools.qwenCode]: (providers) => providers.filter((p) => p.type.includes('openai')),
|
[codeTools.qwenCode]: (providers) => providers.filter((p) => p.type.includes('openai')),
|
||||||
[codeTools.openaiCodex]: (providers) =>
|
[codeTools.openaiCodex]: (providers) =>
|
||||||
providers.filter((p) => p.id === 'openai' || OPENAI_CODEX_SUPPORTED_PROVIDERS.includes(p.id)),
|
providers.filter((p) => p.id === 'openai' || OPENAI_CODEX_SUPPORTED_PROVIDERS.includes(p.id)),
|
||||||
[codeTools.iFlowCli]: (providers) => providers.filter((p) => p.type.includes('openai'))
|
[codeTools.iFlowCli]: (providers) => providers.filter((p) => p.type.includes('openai')),
|
||||||
|
[codeTools.githubCopilotCli]: () => []
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getCodeToolsApiBaseUrl = (model: Model, type: EndpointType) => {
|
export const getCodeToolsApiBaseUrl = (model: Model, type: EndpointType) => {
|
||||||
@ -158,6 +160,10 @@ export const generateToolEnvironment = ({
|
|||||||
env.IFLOW_BASE_URL = baseUrl
|
env.IFLOW_BASE_URL = baseUrl
|
||||||
env.IFLOW_MODEL_NAME = model.id
|
env.IFLOW_MODEL_NAME = model.id
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case codeTools.githubCopilotCli:
|
||||||
|
env.GITHUB_TOKEN = apiKey || ''
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return env
|
return env
|
||||||
|
|||||||
@ -6,11 +6,14 @@ import { useInPlaceEdit } from '@renderer/hooks/useInPlaceEdit'
|
|||||||
import { useKnowledgeBases } from '@renderer/hooks/useKnowledge'
|
import { useKnowledgeBases } from '@renderer/hooks/useKnowledge'
|
||||||
import { useActiveNode } from '@renderer/hooks/useNotesQuery'
|
import { useActiveNode } from '@renderer/hooks/useNotesQuery'
|
||||||
import NotesSidebarHeader from '@renderer/pages/notes/NotesSidebarHeader'
|
import NotesSidebarHeader from '@renderer/pages/notes/NotesSidebarHeader'
|
||||||
import { useAppSelector } from '@renderer/store'
|
import { fetchNoteSummary } from '@renderer/services/ApiService'
|
||||||
|
import { RootState, useAppSelector } from '@renderer/store'
|
||||||
import { selectSortType } from '@renderer/store/note'
|
import { selectSortType } from '@renderer/store/note'
|
||||||
import { NotesSortType, NotesTreeNode } from '@renderer/types/note'
|
import { NotesSortType, NotesTreeNode } from '@renderer/types/note'
|
||||||
|
import { exportNote } from '@renderer/utils/export'
|
||||||
import { useVirtualizer } from '@tanstack/react-virtual'
|
import { useVirtualizer } from '@tanstack/react-virtual'
|
||||||
import { Dropdown, Input, InputRef, MenuProps } from 'antd'
|
import { Dropdown, Input, InputRef, MenuProps } from 'antd'
|
||||||
|
import { ItemType, MenuItemType } from 'antd/es/menu/interface'
|
||||||
import {
|
import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
@ -20,11 +23,14 @@ import {
|
|||||||
FileSearch,
|
FileSearch,
|
||||||
Folder,
|
Folder,
|
||||||
FolderOpen,
|
FolderOpen,
|
||||||
|
Sparkles,
|
||||||
Star,
|
Star,
|
||||||
StarOff
|
StarOff,
|
||||||
|
UploadIcon
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { FC, memo, Ref, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { FC, memo, Ref, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
interface NotesSidebarProps {
|
interface NotesSidebarProps {
|
||||||
@ -50,6 +56,8 @@ interface TreeNodeProps {
|
|||||||
selectedFolderId?: string | null
|
selectedFolderId?: string | null
|
||||||
activeNodeId?: string
|
activeNodeId?: string
|
||||||
editingNodeId: string | null
|
editingNodeId: string | null
|
||||||
|
renamingNodeIds: Set<string>
|
||||||
|
newlyRenamedNodeIds: Set<string>
|
||||||
draggedNodeId: string | null
|
draggedNodeId: string | null
|
||||||
dragOverNodeId: string | null
|
dragOverNodeId: string | null
|
||||||
dragPosition: 'before' | 'inside' | 'after'
|
dragPosition: 'before' | 'inside' | 'after'
|
||||||
@ -72,6 +80,8 @@ const TreeNode = memo<TreeNodeProps>(
|
|||||||
selectedFolderId,
|
selectedFolderId,
|
||||||
activeNodeId,
|
activeNodeId,
|
||||||
editingNodeId,
|
editingNodeId,
|
||||||
|
renamingNodeIds,
|
||||||
|
newlyRenamedNodeIds,
|
||||||
draggedNodeId,
|
draggedNodeId,
|
||||||
dragOverNodeId,
|
dragOverNodeId,
|
||||||
dragPosition,
|
dragPosition,
|
||||||
@ -92,6 +102,8 @@ const TreeNode = memo<TreeNodeProps>(
|
|||||||
? node.type === 'folder' && node.id === selectedFolderId
|
? node.type === 'folder' && node.id === selectedFolderId
|
||||||
: node.id === activeNodeId
|
: node.id === activeNodeId
|
||||||
const isEditing = editingNodeId === node.id && inPlaceEdit.isEditing
|
const isEditing = editingNodeId === node.id && inPlaceEdit.isEditing
|
||||||
|
const isRenaming = renamingNodeIds.has(node.id)
|
||||||
|
const isNewlyRenamed = newlyRenamedNodeIds.has(node.id)
|
||||||
const hasChildren = node.children && node.children.length > 0
|
const hasChildren = node.children && node.children.length > 0
|
||||||
const isDragging = draggedNodeId === node.id
|
const isDragging = draggedNodeId === node.id
|
||||||
const isDragOver = dragOverNodeId === node.id
|
const isDragOver = dragOverNodeId === node.id
|
||||||
@ -99,6 +111,12 @@ const TreeNode = memo<TreeNodeProps>(
|
|||||||
const isDragInside = isDragOver && dragPosition === 'inside'
|
const isDragInside = isDragOver && dragPosition === 'inside'
|
||||||
const isDragAfter = isDragOver && dragPosition === 'after'
|
const isDragAfter = isDragOver && dragPosition === 'after'
|
||||||
|
|
||||||
|
const getNodeNameClassName = () => {
|
||||||
|
if (isRenaming) return 'shimmer'
|
||||||
|
if (isNewlyRenamed) return 'typing'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={node.id}>
|
<div key={node.id}>
|
||||||
<Dropdown menu={{ items: getMenuItems(node) }} trigger={['contextMenu']}>
|
<Dropdown menu={{ items: getMenuItems(node) }} trigger={['contextMenu']}>
|
||||||
@ -156,7 +174,7 @@ const TreeNode = memo<TreeNodeProps>(
|
|||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<NodeName>{node.name}</NodeName>
|
<NodeName className={getNodeNameClassName()}>{node.name}</NodeName>
|
||||||
)}
|
)}
|
||||||
</TreeNodeContent>
|
</TreeNodeContent>
|
||||||
</TreeNodeContainer>
|
</TreeNodeContainer>
|
||||||
@ -173,6 +191,8 @@ const TreeNode = memo<TreeNodeProps>(
|
|||||||
selectedFolderId={selectedFolderId}
|
selectedFolderId={selectedFolderId}
|
||||||
activeNodeId={activeNodeId}
|
activeNodeId={activeNodeId}
|
||||||
editingNodeId={editingNodeId}
|
editingNodeId={editingNodeId}
|
||||||
|
renamingNodeIds={renamingNodeIds}
|
||||||
|
newlyRenamedNodeIds={newlyRenamedNodeIds}
|
||||||
draggedNodeId={draggedNodeId}
|
draggedNodeId={draggedNodeId}
|
||||||
dragOverNodeId={dragOverNodeId}
|
dragOverNodeId={dragOverNodeId}
|
||||||
dragPosition={dragPosition}
|
dragPosition={dragPosition}
|
||||||
@ -213,7 +233,10 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
const { bases } = useKnowledgeBases()
|
const { bases } = useKnowledgeBases()
|
||||||
const { activeNode } = useActiveNode(notesTree)
|
const { activeNode } = useActiveNode(notesTree)
|
||||||
const sortType = useAppSelector(selectSortType)
|
const sortType = useAppSelector(selectSortType)
|
||||||
|
const exportMenuOptions = useSelector((state: RootState) => state.settings.exportMenuOptions)
|
||||||
const [editingNodeId, setEditingNodeId] = useState<string | null>(null)
|
const [editingNodeId, setEditingNodeId] = useState<string | null>(null)
|
||||||
|
const [renamingNodeIds, setRenamingNodeIds] = useState<Set<string>>(new Set())
|
||||||
|
const [newlyRenamedNodeIds, setNewlyRenamedNodeIds] = useState<Set<string>>(new Set())
|
||||||
const [draggedNodeId, setDraggedNodeId] = useState<string | null>(null)
|
const [draggedNodeId, setDraggedNodeId] = useState<string | null>(null)
|
||||||
const [dragOverNodeId, setDragOverNodeId] = useState<string | null>(null)
|
const [dragOverNodeId, setDragOverNodeId] = useState<string | null>(null)
|
||||||
const [dragPosition, setDragPosition] = useState<'before' | 'inside' | 'after'>('inside')
|
const [dragPosition, setDragPosition] = useState<'before' | 'inside' | 'after'>('inside')
|
||||||
@ -336,6 +359,49 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
[bases.length, t]
|
[bases.length, t]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleAutoRename = useCallback(
|
||||||
|
async (note: NotesTreeNode) => {
|
||||||
|
if (note.type !== 'file') return
|
||||||
|
|
||||||
|
setRenamingNodeIds((prev) => new Set(prev).add(note.id))
|
||||||
|
try {
|
||||||
|
const content = await window.api.file.readExternal(note.externalPath)
|
||||||
|
if (!content || content.trim().length === 0) {
|
||||||
|
window.toast.warning(t('notes.auto_rename.empty_note'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const summaryText = await fetchNoteSummary({ content })
|
||||||
|
if (summaryText) {
|
||||||
|
onRenameNode(note.id, summaryText)
|
||||||
|
window.toast.success(t('notes.auto_rename.success'))
|
||||||
|
} else {
|
||||||
|
window.toast.error(t('notes.auto_rename.failed'))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
window.toast.error(t('notes.auto_rename.failed'))
|
||||||
|
logger.error(`Failed to auto-rename note: ${error}`)
|
||||||
|
} finally {
|
||||||
|
setRenamingNodeIds((prev) => {
|
||||||
|
const next = new Set(prev)
|
||||||
|
next.delete(note.id)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
|
||||||
|
setNewlyRenamedNodeIds((prev) => new Set(prev).add(note.id))
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setNewlyRenamedNodeIds((prev) => {
|
||||||
|
const next = new Set(prev)
|
||||||
|
next.delete(note.id)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}, 700)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onRenameNode, t]
|
||||||
|
)
|
||||||
|
|
||||||
const handleDragStart = useCallback((e: React.DragEvent, node: NotesTreeNode) => {
|
const handleDragStart = useCallback((e: React.DragEvent, node: NotesTreeNode) => {
|
||||||
setDraggedNodeId(node.id)
|
setDraggedNodeId(node.id)
|
||||||
e.dataTransfer.effectAllowed = 'move'
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
@ -490,7 +556,22 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
|
|
||||||
const getMenuItems = useCallback(
|
const getMenuItems = useCallback(
|
||||||
(node: NotesTreeNode) => {
|
(node: NotesTreeNode) => {
|
||||||
const baseMenuItems: MenuProps['items'] = [
|
const baseMenuItems: MenuProps['items'] = []
|
||||||
|
|
||||||
|
// only show auto rename for file for now
|
||||||
|
if (node.type !== 'folder') {
|
||||||
|
baseMenuItems.push({
|
||||||
|
label: t('notes.auto_rename.label'),
|
||||||
|
key: 'auto-rename',
|
||||||
|
icon: <Sparkles size={14} />,
|
||||||
|
disabled: renamingNodeIds.has(node.id),
|
||||||
|
onClick: () => {
|
||||||
|
handleAutoRename(node)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
baseMenuItems.push(
|
||||||
{
|
{
|
||||||
label: t('notes.rename'),
|
label: t('notes.rename'),
|
||||||
key: 'rename',
|
key: 'rename',
|
||||||
@ -507,7 +588,7 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
window.api.openPath(node.externalPath)
|
window.api.openPath(node.externalPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
)
|
||||||
if (node.type !== 'folder') {
|
if (node.type !== 'folder') {
|
||||||
baseMenuItems.push(
|
baseMenuItems.push(
|
||||||
{
|
{
|
||||||
@ -525,6 +606,48 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
onClick: () => {
|
onClick: () => {
|
||||||
handleExportKnowledge(node)
|
handleExportKnowledge(node)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('chat.topics.export.title'),
|
||||||
|
key: 'export',
|
||||||
|
icon: <UploadIcon size={14} />,
|
||||||
|
children: [
|
||||||
|
exportMenuOptions.markdown && {
|
||||||
|
label: t('chat.topics.export.md.label'),
|
||||||
|
key: 'markdown',
|
||||||
|
onClick: () => exportNote({ node, platform: 'markdown' })
|
||||||
|
},
|
||||||
|
exportMenuOptions.docx && {
|
||||||
|
label: t('chat.topics.export.word'),
|
||||||
|
key: 'word',
|
||||||
|
onClick: () => exportNote({ node, platform: 'docx' })
|
||||||
|
},
|
||||||
|
exportMenuOptions.notion && {
|
||||||
|
label: t('chat.topics.export.notion'),
|
||||||
|
key: 'notion',
|
||||||
|
onClick: () => exportNote({ node, platform: 'notion' })
|
||||||
|
},
|
||||||
|
exportMenuOptions.yuque && {
|
||||||
|
label: t('chat.topics.export.yuque'),
|
||||||
|
key: 'yuque',
|
||||||
|
onClick: () => exportNote({ node, platform: 'yuque' })
|
||||||
|
},
|
||||||
|
exportMenuOptions.obsidian && {
|
||||||
|
label: t('chat.topics.export.obsidian'),
|
||||||
|
key: 'obsidian',
|
||||||
|
onClick: () => exportNote({ node, platform: 'obsidian' })
|
||||||
|
},
|
||||||
|
exportMenuOptions.joplin && {
|
||||||
|
label: t('chat.topics.export.joplin'),
|
||||||
|
key: 'joplin',
|
||||||
|
onClick: () => exportNote({ node, platform: 'joplin' })
|
||||||
|
},
|
||||||
|
exportMenuOptions.siyuan && {
|
||||||
|
label: t('chat.topics.export.siyuan'),
|
||||||
|
key: 'siyuan',
|
||||||
|
onClick: () => exportNote({ node, platform: 'siyuan' })
|
||||||
|
}
|
||||||
|
].filter(Boolean) as ItemType<MenuItemType>[]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -543,7 +666,16 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
|
|
||||||
return baseMenuItems
|
return baseMenuItems
|
||||||
},
|
},
|
||||||
[t, handleStartEdit, onToggleStar, handleExportKnowledge, handleDeleteNode]
|
[
|
||||||
|
t,
|
||||||
|
handleStartEdit,
|
||||||
|
onToggleStar,
|
||||||
|
handleExportKnowledge,
|
||||||
|
handleDeleteNode,
|
||||||
|
renamingNodeIds,
|
||||||
|
handleAutoRename,
|
||||||
|
exportMenuOptions
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleDropFiles = useCallback(
|
const handleDropFiles = useCallback(
|
||||||
@ -680,6 +812,8 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
selectedFolderId={selectedFolderId}
|
selectedFolderId={selectedFolderId}
|
||||||
activeNodeId={activeNode?.id}
|
activeNodeId={activeNode?.id}
|
||||||
editingNodeId={editingNodeId}
|
editingNodeId={editingNodeId}
|
||||||
|
renamingNodeIds={renamingNodeIds}
|
||||||
|
newlyRenamedNodeIds={newlyRenamedNodeIds}
|
||||||
draggedNodeId={draggedNodeId}
|
draggedNodeId={draggedNodeId}
|
||||||
dragOverNodeId={dragOverNodeId}
|
dragOverNodeId={dragOverNodeId}
|
||||||
dragPosition={dragPosition}
|
dragPosition={dragPosition}
|
||||||
@ -724,6 +858,8 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
selectedFolderId={selectedFolderId}
|
selectedFolderId={selectedFolderId}
|
||||||
activeNodeId={activeNode?.id}
|
activeNodeId={activeNode?.id}
|
||||||
editingNodeId={editingNodeId}
|
editingNodeId={editingNodeId}
|
||||||
|
renamingNodeIds={renamingNodeIds}
|
||||||
|
newlyRenamedNodeIds={newlyRenamedNodeIds}
|
||||||
draggedNodeId={draggedNodeId}
|
draggedNodeId={draggedNodeId}
|
||||||
dragOverNodeId={dragOverNodeId}
|
dragOverNodeId={dragOverNodeId}
|
||||||
dragPosition={dragPosition}
|
dragPosition={dragPosition}
|
||||||
@ -746,6 +882,8 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
|||||||
selectedFolderId={selectedFolderId}
|
selectedFolderId={selectedFolderId}
|
||||||
activeNodeId={activeNode?.id}
|
activeNodeId={activeNode?.id}
|
||||||
editingNodeId={editingNodeId}
|
editingNodeId={editingNodeId}
|
||||||
|
renamingNodeIds={renamingNodeIds}
|
||||||
|
newlyRenamedNodeIds={newlyRenamedNodeIds}
|
||||||
draggedNodeId={draggedNodeId}
|
draggedNodeId={draggedNodeId}
|
||||||
dragOverNodeId={dragOverNodeId}
|
dragOverNodeId={dragOverNodeId}
|
||||||
dragPosition={dragPosition}
|
dragPosition={dragPosition}
|
||||||
@ -933,6 +1071,44 @@ const NodeName = styled.div`
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
position: relative;
|
||||||
|
will-change: background-position, width;
|
||||||
|
|
||||||
|
--color-shimmer-mid: var(--color-text-1);
|
||||||
|
--color-shimmer-end: color-mix(in srgb, var(--color-text-1) 25%, transparent);
|
||||||
|
|
||||||
|
&.shimmer {
|
||||||
|
background: linear-gradient(to left, var(--color-shimmer-end), var(--color-shimmer-mid), var(--color-shimmer-end));
|
||||||
|
background-size: 200% 100%;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
animation: shimmer 3s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.typing {
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: typewriter 0.5s steps(40, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% {
|
||||||
|
background-position: 200% 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: -200% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes typewriter {
|
||||||
|
from {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const EditInput = styled(Input)`
|
const EditInput = styled(Input)`
|
||||||
|
|||||||
@ -251,6 +251,68 @@ export async function fetchMessagesSummary({ messages, assistant }: { messages:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchNoteSummary({ content, assistant }: { content: string; assistant?: Assistant }) {
|
||||||
|
let prompt = (getStoreSetting('topicNamingPrompt') as string) || i18n.t('prompts.title')
|
||||||
|
const resolvedAssistant = assistant || getDefaultAssistant()
|
||||||
|
const model = getQuickModel() || resolvedAssistant.model || getDefaultModel()
|
||||||
|
|
||||||
|
if (prompt && containsSupportedVariables(prompt)) {
|
||||||
|
prompt = await replacePromptVariables(prompt, model.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
const provider = getProviderByModel(model)
|
||||||
|
|
||||||
|
if (!hasApiKey(provider)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const AI = new AiProviderNew(model)
|
||||||
|
|
||||||
|
// only 2000 char and no images
|
||||||
|
const truncatedContent = content.substring(0, 2000)
|
||||||
|
const purifiedContent = purifyMarkdownImages(truncatedContent)
|
||||||
|
|
||||||
|
const summaryAssistant = {
|
||||||
|
...resolvedAssistant,
|
||||||
|
settings: {
|
||||||
|
...resolvedAssistant.settings,
|
||||||
|
reasoning_effort: undefined,
|
||||||
|
qwenThinkMode: false
|
||||||
|
},
|
||||||
|
prompt,
|
||||||
|
model
|
||||||
|
}
|
||||||
|
|
||||||
|
const llmMessages = {
|
||||||
|
system: prompt,
|
||||||
|
prompt: purifiedContent
|
||||||
|
}
|
||||||
|
|
||||||
|
const middlewareConfig: AiSdkMiddlewareConfig = {
|
||||||
|
streamOutput: false,
|
||||||
|
enableReasoning: false,
|
||||||
|
isPromptToolUse: false,
|
||||||
|
isSupportedToolUse: false,
|
||||||
|
isImageGenerationEndpoint: false,
|
||||||
|
enableWebSearch: false,
|
||||||
|
enableGenerateImage: false,
|
||||||
|
enableUrlContext: false,
|
||||||
|
mcpTools: []
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { getText } = await AI.completions(model.id, llmMessages, {
|
||||||
|
...middlewareConfig,
|
||||||
|
assistant: summaryAssistant,
|
||||||
|
callType: 'summary'
|
||||||
|
})
|
||||||
|
const text = getText()
|
||||||
|
return removeSpecialCharactersForTopicName(text) || null
|
||||||
|
} catch (error: any) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// export async function fetchSearchSummary({ messages, assistant }: { messages: Message[]; assistant: Assistant }) {
|
// export async function fetchSearchSummary({ messages, assistant }: { messages: Message[]; assistant: Assistant }) {
|
||||||
// const model = getQuickModel() || assistant.model || getDefaultModel()
|
// const model = getQuickModel() || assistant.model || getDefaultModel()
|
||||||
// const provider = getProviderByModel(model)
|
// const provider = getProviderByModel(model)
|
||||||
|
|||||||
@ -26,12 +26,17 @@ export const initialState: CodeToolsState = {
|
|||||||
[codeTools.qwenCode]: null,
|
[codeTools.qwenCode]: null,
|
||||||
[codeTools.claudeCode]: null,
|
[codeTools.claudeCode]: null,
|
||||||
[codeTools.geminiCli]: null,
|
[codeTools.geminiCli]: null,
|
||||||
[codeTools.openaiCodex]: null
|
[codeTools.openaiCodex]: null,
|
||||||
|
[codeTools.iFlowCli]: null,
|
||||||
|
[codeTools.githubCopilotCli]: null
|
||||||
},
|
},
|
||||||
environmentVariables: {
|
environmentVariables: {
|
||||||
'qwen-code': '',
|
'qwen-code': '',
|
||||||
'claude-code': '',
|
'claude-code': '',
|
||||||
'gemini-cli': ''
|
'gemini-cli': '',
|
||||||
|
'openai-codex': '',
|
||||||
|
'iflow-cli': '',
|
||||||
|
'github-copilot-cli': ''
|
||||||
},
|
},
|
||||||
directories: [],
|
directories: [],
|
||||||
currentDirectory: '',
|
currentDirectory: '',
|
||||||
@ -63,7 +68,10 @@ const codeToolsSlice = createSlice({
|
|||||||
state.environmentVariables = {
|
state.environmentVariables = {
|
||||||
'qwen-code': '',
|
'qwen-code': '',
|
||||||
'claude-code': '',
|
'claude-code': '',
|
||||||
'gemini-cli': ''
|
'gemini-cli': '',
|
||||||
|
'openai-codex': '',
|
||||||
|
'iflow-cli': '',
|
||||||
|
'github-copilot-cli': ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.environmentVariables[state.selectedCliTool] = action.payload
|
state.environmentVariables[state.selectedCliTool] = action.payload
|
||||||
|
|||||||
@ -1082,3 +1082,51 @@ export const exportTopicToNotes = async (topic: Topic, folderPath: string): Prom
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const exportNoteAsMarkdown = async (noteName: string, content: string): Promise<void> => {
|
||||||
|
const markdown = `# ${noteName}\n\n${content}`
|
||||||
|
const fileName = removeSpecialCharactersForFileName(noteName) + '.md'
|
||||||
|
const result = await window.api.file.save(fileName, markdown)
|
||||||
|
if (result) {
|
||||||
|
window.toast.success(i18n.t('message.success.markdown.export.specified'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NoteExportOptions {
|
||||||
|
node: { name: string; externalPath: string }
|
||||||
|
platform: 'markdown' | 'docx' | 'notion' | 'yuque' | 'obsidian' | 'joplin' | 'siyuan'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const exportNote = async ({ node, platform }: NoteExportOptions): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const content = await window.api.file.readExternal(node.externalPath)
|
||||||
|
|
||||||
|
switch (platform) {
|
||||||
|
case 'markdown':
|
||||||
|
return await exportNoteAsMarkdown(node.name, content)
|
||||||
|
case 'docx':
|
||||||
|
window.api.export.toWord(`# ${node.name}\n\n${content}`, removeSpecialCharactersForFileName(node.name))
|
||||||
|
return
|
||||||
|
case 'notion':
|
||||||
|
await exportMessageToNotion(node.name, content)
|
||||||
|
return
|
||||||
|
case 'yuque':
|
||||||
|
await exportMarkdownToYuque(node.name, `# ${node.name}\n\n${content}`)
|
||||||
|
return
|
||||||
|
case 'obsidian': {
|
||||||
|
const { default: ObsidianExportPopup } = await import('@renderer/components/Popups/ObsidianExportPopup')
|
||||||
|
await ObsidianExportPopup.show({ title: node.name, processingMethod: '1', rawContent: content })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case 'joplin':
|
||||||
|
await exportMarkdownToJoplin(node.name, content)
|
||||||
|
return
|
||||||
|
case 'siyuan':
|
||||||
|
await exportMarkdownToSiyuan(node.name, `# ${node.name}\n\n${content}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to export note to ${platform}:`, error as Error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
224
yarn.lock
224
yarn.lock
@ -74,169 +74,157 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/amazon-bedrock@npm:^3.0.21":
|
"@ai-sdk/amazon-bedrock@npm:^3.0.29":
|
||||||
version: 3.0.21
|
version: 3.0.29
|
||||||
resolution: "@ai-sdk/amazon-bedrock@npm:3.0.21"
|
resolution: "@ai-sdk/amazon-bedrock@npm:3.0.29"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/anthropic": "npm:2.0.17"
|
"@ai-sdk/anthropic": "npm:2.0.22"
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
"@smithy/eventstream-codec": "npm:^4.0.1"
|
"@smithy/eventstream-codec": "npm:^4.0.1"
|
||||||
"@smithy/util-utf8": "npm:^4.0.0"
|
"@smithy/util-utf8": "npm:^4.0.0"
|
||||||
aws4fetch: "npm:^1.0.20"
|
aws4fetch: "npm:^1.0.20"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/2d15baaad53e389666cede9673e2b43f5299e2cedb70f5b7afc656b7616e73775a9108c2cc1beee4644ff4c66ad41c8dd0b412373dd05caa4fc3d477c4343ea8
|
checksum: 10c0/7add02e6c13774943929bb5d568b3110f6badc6d95cb56c6d3011cafc45778e27c0133417dd7fe835e7f0b1ae7767c22a7d5e3d39f725e2aa44e2b6e47d95fb7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/anthropic@npm:2.0.17, @ai-sdk/anthropic@npm:^2.0.17":
|
"@ai-sdk/anthropic@npm:2.0.22, @ai-sdk/anthropic@npm:^2.0.22":
|
||||||
version: 2.0.17
|
version: 2.0.22
|
||||||
resolution: "@ai-sdk/anthropic@npm:2.0.17"
|
resolution: "@ai-sdk/anthropic@npm:2.0.22"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/783b6a953f3854c4303ad7c30dd56d4706486c7d1151adb17071d87933418c59c26bce53d5c26d34c4d4728eaac4a856ce49a336caed26a7216f982fea562814
|
checksum: 10c0/d922d2ff606b2429fb14c099628ba6734ef7c9b0e9225635f3faaf2d067362dea6ae0e920a35c05ccf15a01c59fef93ead5f147a9609dd3dd8c3ac18a3123b85
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/azure@npm:^2.0.30":
|
"@ai-sdk/azure@npm:^2.0.42":
|
||||||
version: 2.0.30
|
version: 2.0.42
|
||||||
resolution: "@ai-sdk/azure@npm:2.0.30"
|
resolution: "@ai-sdk/azure@npm:2.0.42"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/openai": "npm:2.0.30"
|
"@ai-sdk/openai": "npm:2.0.42"
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/22af450e28026547badc891a627bcb3cfa2d030864089947172506810f06cfa4c74c453aabd6a0d5c05ede5ffdee381b9278772ce781eca0c7c826c7d7ae3dc3
|
checksum: 10c0/14d3d6edac691df57879a9a7efc46d5d00b6bde5b64cd62a67a7668455c341171119ae90a431e57ac37009bced19add50b3da26998376b7e56e080bc2c997c00
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/deepseek@npm:^1.0.17":
|
"@ai-sdk/deepseek@npm:^1.0.20":
|
||||||
version: 1.0.17
|
version: 1.0.20
|
||||||
resolution: "@ai-sdk/deepseek@npm:1.0.17"
|
resolution: "@ai-sdk/deepseek@npm:1.0.20"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/openai-compatible": "npm:1.0.17"
|
"@ai-sdk/openai-compatible": "npm:1.0.19"
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/c408701343bb28ed0b3e034b8789e6de1dfd6cfc6a9b53feb68f155889e29a9fbbcf05bd99e63f60809cf05ee4b158abaccdf1cbcd9df92c0987094220a61d08
|
checksum: 10c0/e66ece8cf6371c2bac5436ed82cd1e2bb5c367fae6df60090f91cff62bf241f4df0abded99c33558013f8dc0bcc7d962f2126086eba8587ba929da50afd3d806
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/gateway@npm:1.0.23":
|
"@ai-sdk/gateway@npm:1.0.32":
|
||||||
version: 1.0.23
|
version: 1.0.32
|
||||||
resolution: "@ai-sdk/gateway@npm:1.0.23"
|
resolution: "@ai-sdk/gateway@npm:1.0.32"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/b1e1a6ab63b9191075eed92c586cd927696f8997ad24f056585aee3f5fffd283d981aa6b071a2560ecda4295445b80a4cfd321fa63c06e7ac54a06bc4c84887f
|
checksum: 10c0/82c98db6e4e8e235e1ff66410318ebe77cc1518ebf06d8d4757b4f30aaa3bf7075d3028816438551fef2f89e2d4c8c26e4efcd9913a06717aee1308dad3ddc30
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/google-vertex@npm:^3.0.27":
|
"@ai-sdk/google-vertex@npm:^3.0.33":
|
||||||
version: 3.0.27
|
version: 3.0.33
|
||||||
resolution: "@ai-sdk/google-vertex@npm:3.0.27"
|
resolution: "@ai-sdk/google-vertex@npm:3.0.33"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/anthropic": "npm:2.0.17"
|
"@ai-sdk/anthropic": "npm:2.0.22"
|
||||||
"@ai-sdk/google": "npm:2.0.14"
|
"@ai-sdk/google": "npm:2.0.17"
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
google-auth-library: "npm:^9.15.0"
|
google-auth-library: "npm:^9.15.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/7017838aef9c04c18ce9acec52eb602ee0a38d68a7496977a3898411f1ac235b2d7776011fa686084b90b0881e65c69596014e5465b8ed0d0e313b5db1f967a7
|
checksum: 10c0/d440e46f702385985a34f2260074eb41cf2516036598039c8c72d6155825114452942c3c012a181da7661341bee9a38958e5f9a53bba145b9c5dc4446411a651
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/google@npm:2.0.14":
|
"@ai-sdk/google@npm:2.0.17":
|
||||||
version: 2.0.14
|
version: 2.0.17
|
||||||
resolution: "@ai-sdk/google@npm:2.0.14"
|
resolution: "@ai-sdk/google@npm:2.0.17"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/2c04839cf58c33514a54c9de8190c363b5cacfbfc8404fea5d2ec36ad0af5ced4fc571f978e7aa35876bd9afae138f4c700d2bc1f64a78a37d0401f6797bf8f3
|
checksum: 10c0/174bcde507e5bf4bf95f20dbe4eaba73870715b13779e320f3df44995606e4d7ccd1e1f4b759d224deaf58bdfc6aa2e43a24dcbe5fa335ddfe91df1b06114218
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.14#~/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch":
|
"@ai-sdk/mistral@npm:^2.0.17":
|
||||||
version: 2.0.14
|
version: 2.0.17
|
||||||
resolution: "@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.14#~/.yarn/patches/@ai-sdk-google-npm-2.0.14-376d8b03cc.patch::version=2.0.14&hash=351f1a"
|
resolution: "@ai-sdk/mistral@npm:2.0.17"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/1ed5a0732a82b981d51f63c6241ed8ee94d5c29a842764db770305cfc2f49ab6e528cac438b5357fc7b02194104c7b76d4390a1dc1d019ace9c174b0849e0da6
|
checksum: 10c0/58a129357c93cc7f2b15b2ba6ccfb9df3fb72e06163641602ea41c858f835cd76985d66665a56e4ed3fa1eb19ca75a83ae12986d466ec41942e9bf13d558c441
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/mistral@npm:^2.0.14":
|
"@ai-sdk/openai-compatible@npm:1.0.19, @ai-sdk/openai-compatible@npm:^1.0.19":
|
||||||
version: 2.0.14
|
version: 1.0.19
|
||||||
resolution: "@ai-sdk/mistral@npm:2.0.14"
|
resolution: "@ai-sdk/openai-compatible@npm:1.0.19"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/420be3a039095830aaf59b6f82c1f986ff4800ba5b9438e1dd85530026a42c9454a6e632b6a1a1839816609f4752d0a19140d8943ad78bb976fb5d6a37714e16
|
checksum: 10c0/5b7b21fb515e829c3d8a499a5760ffc035d9b8220695996110e361bd79e9928859da4ecf1ea072735bcbe4977c6dd0661f543871921692e86f8b5bfef14fe0e5
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/openai-compatible@npm:1.0.17, @ai-sdk/openai-compatible@npm:^1.0.17":
|
"@ai-sdk/openai@npm:2.0.42, @ai-sdk/openai@npm:^2.0.42":
|
||||||
version: 1.0.17
|
version: 2.0.42
|
||||||
resolution: "@ai-sdk/openai-compatible@npm:1.0.17"
|
resolution: "@ai-sdk/openai@npm:2.0.42"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/53ab6111e0f44437a2e268a51fb747600844d85b0cd0d170fb87a7b68af3eb21d7728d7bbf14d71c9fcf36e7a0f94ad75f0ad6b1070e473c867ab08ef84f6564
|
checksum: 10c0/b1ab158aafc86735e53c4621ffe125d469bc1732c533193652768a9f66ecd4d169303ce7ca59069b7baf725da49e55bcf81210848f09f66deaf2a8335399e6d7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/openai@npm:2.0.30, @ai-sdk/openai@npm:^2.0.30":
|
"@ai-sdk/perplexity@npm:^2.0.11":
|
||||||
version: 2.0.30
|
version: 2.0.11
|
||||||
resolution: "@ai-sdk/openai@npm:2.0.30"
|
resolution: "@ai-sdk/perplexity@npm:2.0.11"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/90a57c1b10dac46c0bbe7e16cf9202557fb250d9f0e94a2a5fb7d95b5ea77815a56add78b00238d3823f0313c9b2c42abe865478d28a6196f72b341d32dd40af
|
checksum: 10c0/a8722b68f529b3d1baaa1ba4624c61efe732f22b24dfc20e27afae07bb25d72532bcb62d022191ab5e49df24496af619eabc092a4e6ad293b3fe231ef61b6467
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/perplexity@npm:^2.0.9":
|
"@ai-sdk/provider-utils@npm:3.0.10, @ai-sdk/provider-utils@npm:^3.0.10":
|
||||||
version: 2.0.9
|
version: 3.0.10
|
||||||
resolution: "@ai-sdk/perplexity@npm:2.0.9"
|
resolution: "@ai-sdk/provider-utils@npm:3.0.10"
|
||||||
dependencies:
|
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
|
||||||
peerDependencies:
|
|
||||||
zod: ^3.25.76 || ^4
|
|
||||||
checksum: 10c0/2023aadc26c41430571c4897df79074e7a95a12f2238ad57081355484066bcf9e8dfde1da60fa6af12fc9fb2a195899326f753c69f4913dc005a33367f150349
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@ai-sdk/provider-utils@npm:3.0.9, @ai-sdk/provider-utils@npm:^3.0.9":
|
|
||||||
version: 3.0.9
|
|
||||||
resolution: "@ai-sdk/provider-utils@npm:3.0.9"
|
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@standard-schema/spec": "npm:^1.0.0"
|
"@standard-schema/spec": "npm:^1.0.0"
|
||||||
eventsource-parser: "npm:^3.0.5"
|
eventsource-parser: "npm:^3.0.5"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/f8b659343d7e22ae099f7b6fc514591c0408012eb0aa00f7a912798b6d7d7305cafa8f18a07c7adec0bb5d39d9b6256b76d65c5393c3fc843d1361c52f1f8080
|
checksum: 10c0/d2c16abdb84ba4ef48c9f56190b5ffde224b9e6ae5147c5c713d2623627732d34b96aa9aef2a2ea4b0c49e1b863cc963c7d7ff964a1dc95f0f036097aaaaaa98
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -249,16 +237,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@ai-sdk/xai@npm:^2.0.18":
|
"@ai-sdk/xai@npm:^2.0.23":
|
||||||
version: 2.0.18
|
version: 2.0.23
|
||||||
resolution: "@ai-sdk/xai@npm:2.0.18"
|
resolution: "@ai-sdk/xai@npm:2.0.23"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/openai-compatible": "npm:1.0.17"
|
"@ai-sdk/openai-compatible": "npm:1.0.19"
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/7134501a2d315ec13605558aa24d7f5662885fe8b0491a634abefeb0c5c88517149677d1beff0c8abeec78a6dcd14573a2f57d96fa54a1d63d03820ac7ff827a
|
checksum: 10c0/4cf6b3bc71024797d1b2e37b57fb746f7387f9a7c1da530fd040aad1a840603a1a86fb7df7e428c723eba9b1547f89063d68f84e6e08444d2d4f152dee321dc3
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -2379,14 +2367,14 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@cherrystudio/ai-core@workspace:packages/aiCore"
|
resolution: "@cherrystudio/ai-core@workspace:packages/aiCore"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/anthropic": "npm:^2.0.17"
|
"@ai-sdk/anthropic": "npm:^2.0.22"
|
||||||
"@ai-sdk/azure": "npm:^2.0.30"
|
"@ai-sdk/azure": "npm:^2.0.42"
|
||||||
"@ai-sdk/deepseek": "npm:^1.0.17"
|
"@ai-sdk/deepseek": "npm:^1.0.20"
|
||||||
"@ai-sdk/openai": "npm:^2.0.30"
|
"@ai-sdk/openai": "npm:^2.0.42"
|
||||||
"@ai-sdk/openai-compatible": "npm:^1.0.17"
|
"@ai-sdk/openai-compatible": "npm:^1.0.19"
|
||||||
"@ai-sdk/provider": "npm:^2.0.0"
|
"@ai-sdk/provider": "npm:^2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:^3.0.9"
|
"@ai-sdk/provider-utils": "npm:^3.0.10"
|
||||||
"@ai-sdk/xai": "npm:^2.0.18"
|
"@ai-sdk/xai": "npm:^2.0.23"
|
||||||
tsdown: "npm:^0.12.9"
|
tsdown: "npm:^0.12.9"
|
||||||
typescript: "npm:^5.0.0"
|
typescript: "npm:^5.0.0"
|
||||||
vitest: "npm:^3.2.4"
|
vitest: "npm:^3.2.4"
|
||||||
@ -14193,10 +14181,10 @@ __metadata:
|
|||||||
"@agentic/exa": "npm:^7.3.3"
|
"@agentic/exa": "npm:^7.3.3"
|
||||||
"@agentic/searxng": "npm:^7.3.3"
|
"@agentic/searxng": "npm:^7.3.3"
|
||||||
"@agentic/tavily": "npm:^7.3.3"
|
"@agentic/tavily": "npm:^7.3.3"
|
||||||
"@ai-sdk/amazon-bedrock": "npm:^3.0.21"
|
"@ai-sdk/amazon-bedrock": "npm:^3.0.29"
|
||||||
"@ai-sdk/google-vertex": "npm:^3.0.27"
|
"@ai-sdk/google-vertex": "npm:^3.0.33"
|
||||||
"@ai-sdk/mistral": "npm:^2.0.14"
|
"@ai-sdk/mistral": "npm:^2.0.17"
|
||||||
"@ai-sdk/perplexity": "npm:^2.0.9"
|
"@ai-sdk/perplexity": "npm:^2.0.11"
|
||||||
"@ant-design/v5-patch-for-react-19": "npm:^1.0.3"
|
"@ant-design/v5-patch-for-react-19": "npm:^1.0.3"
|
||||||
"@anthropic-ai/claude-agent-sdk": "patch:@anthropic-ai/claude-agent-sdk@npm%3A0.1.1#~/.yarn/patches/@anthropic-ai-claude-agent-sdk-npm-0.1.1-d937b73fed.patch"
|
"@anthropic-ai/claude-agent-sdk": "patch:@anthropic-ai/claude-agent-sdk@npm%3A0.1.1#~/.yarn/patches/@anthropic-ai-claude-agent-sdk-npm-0.1.1-d937b73fed.patch"
|
||||||
"@anthropic-ai/sdk": "npm:^0.41.0"
|
"@anthropic-ai/sdk": "npm:^0.41.0"
|
||||||
@ -14318,7 +14306,7 @@ __metadata:
|
|||||||
"@viz-js/lang-dot": "npm:^1.0.5"
|
"@viz-js/lang-dot": "npm:^1.0.5"
|
||||||
"@viz-js/viz": "npm:^3.14.0"
|
"@viz-js/viz": "npm:^3.14.0"
|
||||||
"@xyflow/react": "npm:^12.4.4"
|
"@xyflow/react": "npm:^12.4.4"
|
||||||
ai: "npm:^5.0.44"
|
ai: "npm:^5.0.59"
|
||||||
antd: "patch:antd@npm%3A5.27.0#~/.yarn/patches/antd-npm-5.27.0-aa91c36546.patch"
|
antd: "patch:antd@npm%3A5.27.0#~/.yarn/patches/antd-npm-5.27.0-aa91c36546.patch"
|
||||||
archiver: "npm:^7.0.1"
|
archiver: "npm:^7.0.1"
|
||||||
async-mutex: "npm:^0.5.0"
|
async-mutex: "npm:^0.5.0"
|
||||||
@ -14581,17 +14569,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"ai@npm:^5.0.44":
|
"ai@npm:^5.0.59":
|
||||||
version: 5.0.44
|
version: 5.0.59
|
||||||
resolution: "ai@npm:5.0.44"
|
resolution: "ai@npm:5.0.59"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ai-sdk/gateway": "npm:1.0.23"
|
"@ai-sdk/gateway": "npm:1.0.32"
|
||||||
"@ai-sdk/provider": "npm:2.0.0"
|
"@ai-sdk/provider": "npm:2.0.0"
|
||||||
"@ai-sdk/provider-utils": "npm:3.0.9"
|
"@ai-sdk/provider-utils": "npm:3.0.10"
|
||||||
"@opentelemetry/api": "npm:1.9.0"
|
"@opentelemetry/api": "npm:1.9.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.25.76 || ^4
|
zod: ^3.25.76 || ^4.1.8
|
||||||
checksum: 10c0/528c7e165f75715194204051ce0aa341d8dca7d5536c2abcf3df83ccda7399ed5d91deaa45a81340f93d2461b1c2fc5f740f7804dfd396927c71b0667403569b
|
checksum: 10c0/daa956e753b93fbc30afbfba5be2ebb73e3c280dae3064e13949f04d5a22c0f4ea5698cc87e24a23ed6585d9cf7febee61b915292dbbd4286dc40c449cf2b845
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -17990,16 +17978,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"electron@npm:37.4.0":
|
"electron@npm:37.6.0":
|
||||||
version: 37.4.0
|
version: 37.6.0
|
||||||
resolution: "electron@npm:37.4.0"
|
resolution: "electron@npm:37.6.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@electron/get": "npm:^2.0.0"
|
"@electron/get": "npm:^2.0.0"
|
||||||
"@types/node": "npm:^22.7.7"
|
"@types/node": "npm:^22.7.7"
|
||||||
extract-zip: "npm:^2.0.1"
|
extract-zip: "npm:^2.0.1"
|
||||||
bin:
|
bin:
|
||||||
electron: cli.js
|
electron: cli.js
|
||||||
checksum: 10c0/92a0c41190e234d302bc612af6cce9af08cd07f6699c1ff21a9365297e73dc9d88c6c4c25ddabf352447e3e555878d2ab0f2f31a14e210dda6de74d2787ff323
|
checksum: 10c0/d67b7f0ff902f9184c2a7445507746343f8b39f3616d9d26128e7515e0184252cfc8ac97a3f1458f9ea9b4af6ab5b3208282014e8d91c0e1505ff21f5fa57ce6
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user