mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-26 02:04:37 +08:00
feat(web): enable AI page data loading in web mode via HTTP endpoints
- Move assistant/skill MD parsers to @openchatlab/node-runtime (shared) - Move BUILTIN_TOOL_CATALOG, provider-registry, model-catalog to @openchatlab/core - Add /_web/ai/* HTTP routes for assistants, skills, LLM config, providers, models, tools - Frontend shims: read ops call HTTP, write/agent ops return stubs - Remove IS_ELECTRON guard in llm.ts to enable web-mode config loading - Improve web-mode AI chat error message to be user-friendly
This commit is contained in:
1 parent
4b15d65b13
commit
12f9ae90eb
23 files changed
+1879
-1372
No files matched your search
@@ -6,6 +6,7 @@
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"dependencies": {
|
||||
"@openchatlab/core": "workspace:*"
|
||||
"@openchatlab/core": "workspace:*",
|
||||
"gray-matter": "^4.0.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 助手 MD 文件解析器(平台无关,Node.js 实现)
|
||||
*/
|
||||
|
||||
import * as path from 'path'
|
||||
import matter from 'gray-matter'
|
||||
import type { AssistantConfig } from './types'
|
||||
|
||||
export function parseAssistantFile(content: string, filePath: string): AssistantConfig | null {
|
||||
try {
|
||||
const { data: fm, content: body } = matter(content)
|
||||
|
||||
const id = fm.id ?? path.basename(filePath, '.md')
|
||||
const name = fm.name
|
||||
if (!name) return null
|
||||
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
systemPrompt: body.trim(),
|
||||
presetQuestions: parseStringArray(fm.presetQuestions),
|
||||
allowedBuiltinTools: parseStringArray(fm.allowedBuiltinTools),
|
||||
builtinId: typeof fm.builtinId === 'string' ? fm.builtinId : undefined,
|
||||
applicableChatTypes: parseChatTypes(fm.applicableChatTypes),
|
||||
supportedLocales: parseStringArray(fm.supportedLocales),
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeAssistant(config: AssistantConfig): string {
|
||||
const fm: Record<string, unknown> = {
|
||||
id: config.id,
|
||||
name: config.name,
|
||||
}
|
||||
|
||||
if (config.builtinId) fm.builtinId = config.builtinId
|
||||
if (config.applicableChatTypes?.length) fm.applicableChatTypes = config.applicableChatTypes
|
||||
if (config.supportedLocales?.length) fm.supportedLocales = config.supportedLocales
|
||||
if (config.allowedBuiltinTools?.length) fm.allowedBuiltinTools = config.allowedBuiltinTools
|
||||
if (config.presetQuestions?.length) fm.presetQuestions = config.presetQuestions
|
||||
|
||||
return matter.stringify(`\n${config.systemPrompt}\n`, fm)
|
||||
}
|
||||
|
||||
function parseStringArray(raw: unknown): string[] {
|
||||
if (Array.isArray(raw)) return raw.map(String).filter(Boolean)
|
||||
return []
|
||||
}
|
||||
|
||||
function parseChatTypes(raw: unknown): ('group' | 'private')[] | undefined {
|
||||
if (!Array.isArray(raw)) return undefined
|
||||
const valid = raw.filter((v): v is 'group' | 'private' => v === 'group' || v === 'private')
|
||||
return valid.length > 0 ? valid : undefined
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* AI 模块(Node.js 实现)
|
||||
*
|
||||
* 助手/技能 MD 文件解析器和共享类型。
|
||||
*/
|
||||
|
||||
export type { AssistantConfig, AssistantSummary, SkillDef, SkillSummary } from './types'
|
||||
export { parseAssistantFile, serializeAssistant } from './assistant-parser'
|
||||
export { parseSkillFile, extractSkillId } from './skill-parser'
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 技能 MD 文件解析器(平台无关,Node.js 实现)
|
||||
*/
|
||||
|
||||
import * as path from 'path'
|
||||
import matter from 'gray-matter'
|
||||
import type { SkillDef } from './types'
|
||||
|
||||
export function parseSkillFile(content: string, filePath: string): SkillDef | null {
|
||||
try {
|
||||
const { data: fm, content: prompt } = matter(content)
|
||||
|
||||
const id = fm.id ?? path.basename(filePath, '.md')
|
||||
const name = fm.name
|
||||
if (!name) return null
|
||||
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
description: fm.description ?? '',
|
||||
tags: parseTags(fm.tags),
|
||||
chatScope: validateChatScope(fm.chatScope),
|
||||
tools: Array.isArray(fm.tools) ? fm.tools : [],
|
||||
prompt: prompt.trim(),
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function extractSkillId(content: string, filePath: string): string | null {
|
||||
try {
|
||||
const { data: fm } = matter(content)
|
||||
return fm.id ?? path.basename(filePath, '.md')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function parseTags(raw: unknown): string[] {
|
||||
if (typeof raw === 'string') {
|
||||
return raw
|
||||
.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
if (Array.isArray(raw)) return raw.map(String).filter(Boolean)
|
||||
return []
|
||||
}
|
||||
|
||||
function validateChatScope(raw: unknown): 'all' | 'group' | 'private' {
|
||||
if (raw === 'group' || raw === 'private') return raw
|
||||
return 'all'
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* AI 模块共享类型(助手、技能的解析结构)
|
||||
*
|
||||
* 与 Electron 端 types.ts 的同名接口结构一致(TypeScript 结构类型兼容)。
|
||||
*/
|
||||
|
||||
export interface AssistantConfig {
|
||||
id: string
|
||||
name: string
|
||||
systemPrompt: string
|
||||
presetQuestions: string[]
|
||||
allowedBuiltinTools?: string[]
|
||||
builtinId?: string
|
||||
applicableChatTypes?: ('group' | 'private')[]
|
||||
supportedLocales?: string[]
|
||||
}
|
||||
|
||||
export interface AssistantSummary {
|
||||
id: string
|
||||
name: string
|
||||
systemPrompt: string
|
||||
presetQuestions: string[]
|
||||
builtinId?: string
|
||||
applicableChatTypes?: ('group' | 'private')[]
|
||||
supportedLocales?: string[]
|
||||
}
|
||||
|
||||
export interface SkillDef {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
tags: string[]
|
||||
chatScope: 'all' | 'group' | 'private'
|
||||
prompt: string
|
||||
tools: string[]
|
||||
builtinId?: string
|
||||
}
|
||||
|
||||
export interface SkillSummary {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
tags: string[]
|
||||
chatScope: 'all' | 'group' | 'private'
|
||||
tools: string[]
|
||||
builtinId?: string
|
||||
}
|
||||
@@ -29,3 +29,7 @@ export {
|
||||
deleteDict,
|
||||
ensureDefaultDict,
|
||||
} from './nlp'
|
||||
|
||||
// AI 助手/技能解析器
|
||||
export type { AssistantConfig, AssistantSummary, SkillDef, SkillSummary } from './ai'
|
||||
export { parseAssistantFile, serializeAssistant, parseSkillFile, extractSkillId } from './ai'
|
||||
Reference in new issue
Block a user