Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

### 修复

- ima 知识库挂接对齐官方 Agent Interface(https://ima.qq.com/agent-interface,落地页现发 ima-skill 1.1.10):用 Client ID / API Key 连接后,按 `search_knowledge_base` + `get_knowledge_base` 选知识库,按 `get_knowledge_list` / `search_knowledge` 自选文档;文件夹 ID 用接口返回的 `media_id`(不自造 `folder_` 前缀),选择会写入挂接。
- 模型页注册的本机 Ollama / GGUF 现在会出现在对话区模型选择器里,并走本机 `127.0.0.1:11434` 调用。注册会补齐 Ollama 供应商默认值、打开服务开关、热加载对话列表;Ollama 未启动时给出可操作错误,而不是静默失败。
- 对话模型选择器底部增加「模型管理」,跳到侧栏同一条「模型」路由(`/models`)。
- 组织页默认只显示本机 openXYOS 嵌入和顶部蓝色状态栏;「启用组织模块」「组织OS管理」在状态栏右下。互生长导入/导出、源码下载、循环时间线等收到「组织OS管理」。iframe 高度等于可见预览区,openXYOS 弹窗按 iframe 视口水平垂直居中。
Expand Down
24 changes: 24 additions & 0 deletions dashboard/src/api/modules/knowledgeBases.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,30 @@ describe("knowledgeBasesApi", () => {
);
});

it("uses official ima Agent Interface endpoints", () => {
knowledgeBasesApi.imaStatus();
knowledgeBasesApi.imaConnect({ client_id: "c", api_key: "k" });
knowledgeBasesApi.imaListBases({ query: "work" });
knowledgeBasesApi.imaListDocuments("kb-ima", {
folder_id: "fd1",
query: "周报",
});

expect(request).toHaveBeenNthCalledWith(1, "/knowledge-bases/ima/status");
expect(request).toHaveBeenNthCalledWith(2, "/knowledge-bases/ima/connect", {
method: "POST",
body: JSON.stringify({ client_id: "c", api_key: "k" }),
});
expect(request).toHaveBeenNthCalledWith(
3,
"/knowledge-bases/ima/bases?query=work",
);
expect(request).toHaveBeenNthCalledWith(
4,
"/knowledge-bases/ima/bases/kb-ima/documents?folder_id=fd1&query=%E5%91%A8%E6%8A%A5",
);
});

it("forwards the upload progress handler", () => {
const file = new File(["document"], "notes.md", { type: "text/markdown" });
const onProgress = vi.fn();
Expand Down
166 changes: 140 additions & 26 deletions dashboard/src/api/modules/knowledgeBases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,73 @@ export interface KnowledgeOcrOptions {
}[];
}

export interface ImaConnectorSummary {
instance_id: string;
display_name: string;
client_id_preview: string;
}

export interface ImaStatus {
connected: boolean;
instance_id: string;
client_id_preview: string;
auth_url: string;
instances: ImaConnectorSummary[];
}

export interface ImaKnowledgeBase {
id: string;
name: string;
cover_url?: string;
description?: string;
}

export interface ImaKnowledgeDoc {
media_id: string;
title: string;
parent_folder_id?: string;
}

export interface ImaKnowledgeFolder {
folder_id: string;
name: string;
parent_folder_id?: string;
file_number?: number;
folder_number?: number;
is_folder: true;
}

export interface ImaSelectedBase {
id: string;
name: string;
}

export interface ImaSelectedDoc {
knowledge_base_id: string;
knowledge_base_name?: string;
media_id: string;
title: string;
}

export interface KnowledgeMountInfo {
mounted: boolean;
readonly?: boolean;
kind?: "local" | "cloud";
source_path?: string;
distill_path?: string;
cloud_url?: string;
cloud_provider?: string;
connector_instance_id?: string;
selected_bases?: ImaSelectedBase[];
selected_docs?: ImaSelectedDoc[];
entries: Array<{
path: string;
name: string;
is_dir: boolean;
size: number;
}>;
}

export interface KnowledgeOnnxDownloadState {
status: "idle" | "downloading" | "loading" | "done" | "failed";
progress: number;
Expand Down Expand Up @@ -315,22 +382,73 @@ export const knowledgeBasesApi = {
},
),

imaStatus: () => request<ImaStatus>("/knowledge-bases/ima/status"),

imaConnect: (body: {
client_id?: string;
api_key?: string;
instance_id?: string;
}) =>
request<ImaStatus>("/knowledge-bases/ima/connect", {
method: "POST",
body: JSON.stringify(body),
}),

imaListBases: (opts?: {
query?: string;
cursor?: string;
limit?: number;
instance_id?: string;
}) => {
const params = new URLSearchParams();
if (opts?.query) params.set("query", opts.query);
if (opts?.cursor) params.set("cursor", opts.cursor);
if (opts?.limit) params.set("limit", String(opts.limit));
if (opts?.instance_id) params.set("instance_id", opts.instance_id);
const qs = params.toString();
return request<{
items: ImaKnowledgeBase[];
next_cursor: string;
is_end: boolean;
}>(qs ? `/knowledge-bases/ima/bases?${qs}` : "/knowledge-bases/ima/bases");
},

imaListDocuments: (
imaKbId: string,
opts?: {
folder_id?: string;
cursor?: string;
limit?: number;
query?: string;
instance_id?: string;
},
) => {
const params = new URLSearchParams();
if (opts?.folder_id) params.set("folder_id", opts.folder_id);
if (opts?.cursor) params.set("cursor", opts.cursor);
if (opts?.limit) params.set("limit", String(opts.limit));
if (opts?.query) params.set("query", opts.query);
if (opts?.instance_id) params.set("instance_id", opts.instance_id);
const qs = params.toString();
return request<{
items: ImaKnowledgeDoc[];
folders: ImaKnowledgeFolder[];
current_path: ImaKnowledgeFolder[];
next_cursor: string;
is_end: boolean;
knowledge_base_id: string;
folder_id: string;
}>(
qs
? `/knowledge-bases/ima/bases/${encodeURIComponent(
imaKbId,
)}/documents?${qs}`
: `/knowledge-bases/ima/bases/${encodeURIComponent(imaKbId)}/documents`,
);
},

getMount: (id: string) =>
request<{
mounted: boolean;
readonly?: boolean;
kind?: "local" | "cloud";
source_path?: string;
distill_path?: string;
cloud_url?: string;
cloud_provider?: string;
entries: Array<{
path: string;
name: string;
is_dir: boolean;
size: number;
}>;
}>(`/knowledge-bases/${id}/mount`),
request<KnowledgeMountInfo>(`/knowledge-bases/${id}/mount`),

setMount: (
id: string,
Expand All @@ -340,26 +458,22 @@ export const knowledgeBasesApi = {
kind?: "local" | "cloud";
cloud_url?: string;
cloud_provider?: string;
connector_instance_id?: string;
selected_bases?: ImaSelectedBase[];
selected_docs?: ImaSelectedDoc[];
},
) =>
request<{
mounted: boolean;
source_path: string;
distill_path: string;
entries: Array<{
path: string;
name: string;
is_dir: boolean;
size: number;
}>;
}>(`/knowledge-bases/${id}/mount`, {
request<KnowledgeMountInfo>(`/knowledge-bases/${id}/mount`, {
method: "PUT",
body: JSON.stringify({
source_path,
distill_path: distill_path || "",
kind: extra?.kind || "local",
cloud_url: extra?.cloud_url || "",
cloud_provider: extra?.cloud_provider || "",
connector_instance_id: extra?.connector_instance_id || "",
selected_bases: extra?.selected_bases || [],
selected_docs: extra?.selected_docs || [],
}),
}),

Expand Down
45 changes: 39 additions & 6 deletions dashboard/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@
"KNOWLEDGE_NAME_TAKEN": "You already have a knowledge base with this name.",
"KNOWLEDGE_NAME_INVALID": "Invalid name.",
"KNOWLEDGE_MOUNT_INVALID": "The knowledge-base folder path is invalid or the directory does not exist.",
"KNOWLEDGE_IMA_AUTH": "IMA Agent Interface authentication failed. Check the Client ID and API Key from https://ima.qq.com/agent-interface.",
"KNOWLEDGE_IMA_UNAVAILABLE": "IMA knowledge API request failed: {{reason}}",
"AVATAR_INVALID": "Avatar must be a PNG, JPEG, WebP, or GIF image.",
"AVATAR_TOO_LARGE": "Avatar exceeds the 5 MB size limit.",
"INVITE_INVALID": "This invite code is invalid.",
Expand Down Expand Up @@ -460,7 +462,7 @@
"settingsLead": "Advanced switch. It is on by default on the desktop. Turning it off hides knowledge bases from colleagues.",
"settingsOpen": "Knowledge base feature",
"homeTitle": "Mount your sources first",
"homeDesc": "Pick a folder on this computer, or attach a cloud knowledge base such as ima. The original folder stays read-only. Parse results go to a new folder you choose. Colleagues can look them up in chat.",
"homeDesc": "Pick a folder on this computer, or connect ima with the official Agent Interface and choose which knowledge bases and documents to attach. The original folder stays read-only. Parse results go to a new folder you choose. Colleagues can look them up in chat.",
"homeAdvancedHint": "Vector, image, and scanned-document OCR settings are for later cleaning, labeling, and distillation.",
"defaultLocalName": "Local files",
"defaultCloudName": "Cloud knowledge",
Expand Down Expand Up @@ -513,17 +515,48 @@
"distillDest": "Choose a new folder for parse results (not the mounted folder)",
"distillDone": "Copied {{count}} files to {{path}}",
"distillFailed": "Could not copy parse results",
"cloudMountTitle": "Cloud knowledge mount",
"cloudMountHint": "Attach a cloud knowledge base such as ima. Files are not parsed locally. Distillation writes a pointer into a new folder you choose, for later assistant strengthening.",
"cloudMountTitle": "ima knowledge mount",
"cloudMountHint": "Connect with the official IMA Agent Interface (Client ID + API Key), then choose which knowledge bases and documents to attach. Files stay on ima; distillation writes a pointer into a folder you choose.",
"cloudUrl": "Cloud knowledge URL",
"cloudUrlRequired": "Enter a cloud knowledge URL",
"cloudProvider": "Provider (ima…)",
"cloudMountAction": "Mount cloud KB",
"cloudMountSaved": "Cloud knowledge attached (no local parse)",
"cloudMountFailed": "Could not attach the cloud knowledge base",
"cloudMountAction": "Save ima selection",
"cloudMountSaved": "ima knowledge bases and documents attached",
"cloudMountFailed": "Could not attach the ima knowledge base",
"cloudAttachAction": "Attach for distillation",
"cloudDistillDest": "Choose a new folder for the cloud pointer (not a local parse)",
"cloudDistillDone": "Wrote a cloud pointer to {{path}}",
"imaAuthTitle": "Official IMA Agent Interface",
"imaAuthHint": "Open the IMA setup page, generate a Client ID and API Key (the key is shown once), then paste them here. FreeOS uses the official ima-openapi headers only.",
"imaAuthLink": "Open ima.qq.com/agent-interface",
"imaClientId": "Client ID",
"imaApiKey": "API Key",
"imaClientIdRequired": "Enter the IMA Client ID",
"imaApiKeyRequired": "Enter the IMA API Key",
"imaConnectAction": "Connect ima",
"imaReconnectAction": "Use this connection",
"imaConnected": "Connected to ima ({{preview}})",
"imaChangeCredentials": "Change credentials",
"imaExistingConnection": "Existing IMA connection",
"imaExistingPlaceholder": "Select a saved IMA connection",
"imaSearchBases": "Search ima knowledge bases",
"imaSearchDocs": "Search documents in this knowledge base",
"imaDocsSearchEmpty": "No matching documents.",
"imaSelectBases": "Select knowledge bases",
"imaSelectDocs": "Select documents",
"imaBasesEmpty": "No ima knowledge bases yet. Create one in ima, then refresh.",
"imaDocsEmpty": "No documents in this folder.",
"imaLoadBasesFailed": "Could not load ima knowledge bases",
"imaLoadDocsFailed": "Could not load ima documents",
"imaSelectionRequired": "Select at least one knowledge base or document",
"imaSelectionSummary": "{{bases}} knowledge bases, {{docs}} documents selected",
"imaWholeBaseHint": "No documents selected in this knowledge base — the whole base will be used.",
"imaRefresh": "Refresh",
"imaLoadMore": "Load more",
"imaFolder": "Folder",
"imaRootFolder": "Root",
"imaSelectAllDocs": "Select all on this page",
"imaClearDocs": "Clear documents",
"backToList": "Back to list",
"createdBy": "Created by {{name}}",
"baseId": "Knowledge base ID",
Expand Down
45 changes: 39 additions & 6 deletions dashboard/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@
"KNOWLEDGE_NAME_TAKEN": "您已有同名知识库。",
"KNOWLEDGE_NAME_INVALID": "名称无效。",
"KNOWLEDGE_MOUNT_INVALID": "知识库文件夹路径无效,或目录不存在。",
"KNOWLEDGE_IMA_AUTH": "IMA Agent Interface 认证失败。请检查 https://ima.qq.com/agent-interface 上的 Client ID 与 API Key。",
"KNOWLEDGE_IMA_UNAVAILABLE": "IMA 知识库接口请求失败:{{reason}}",
"AVATAR_INVALID": "头像须为 PNG、JPEG、WebP 或 GIF 图片。",
"AVATAR_TOO_LARGE": "头像超过 5 MB 大小限制。",
"INVITE_INVALID": "邀请码无效。",
Expand Down Expand Up @@ -460,7 +462,7 @@
"settingsLead": "高级开关。桌面默认已打开。关掉后,同事都无法再使用知识库。",
"settingsOpen": "知识库功能",
"homeTitle": "先把资料挂进来",
"homeDesc": "选电脑上的文件夹,或挂接 ima 等云知识库。原来的文件夹只读,不会被改动;解析结果会存到你另选的新文件夹。同事可以在对话里查阅。",
"homeDesc": "选电脑上的文件夹,或通过官方 IMA Agent Interface 连接 ima,再自选要接入的知识库和文档。原来的文件夹只读,不会被改动;解析结果会存到你另选的新文件夹。同事可以在对话里查阅。",
"homeAdvancedHint": "向量模型、图片和扫描件 OCR 留给后续清洗、标注、蒸馏。",
"defaultLocalName": "本地资料",
"defaultCloudName": "云知识库",
Expand Down Expand Up @@ -513,17 +515,48 @@
"distillDest": "请选择保存解析结果的新文件夹(不能是已挂载的目录)",
"distillDone": "已复制 {{count}} 个文件到 {{path}}",
"distillFailed": "无法复制解析结果",
"cloudMountTitle": "云知识库挂接",
"cloudMountHint": "挂接 ima 等云知识库。不会在本地解析原文。蒸馏时只在你另选的新文件夹写入指针,供以后强化智能助手。",
"cloudMountTitle": "ima 知识库挂接",
"cloudMountHint": "通过官方 IMA Agent Interface(Client ID + API Key)连接,再自选要接入的知识库和文档。原文仍在 ima;蒸馏只在你另选的文件夹写入指针。",
"cloudUrl": "云知识库地址",
"cloudUrlRequired": "请输入云知识库地址",
"cloudProvider": "来源(ima…)",
"cloudMountAction": "挂载云知识库",
"cloudMountSaved": "已挂载云知识库(不做本地解析)",
"cloudMountFailed": "无法挂载云知识库",
"cloudMountAction": "保存 ima 选择",
"cloudMountSaved": "已挂接所选 ima 知识库和文档",
"cloudMountFailed": "无法挂接 ima 知识库",
"cloudAttachAction": "挂到蒸馏目录",
"cloudDistillDest": "请选择保存云知识库指针的新文件夹(不会本地解析原文)",
"cloudDistillDone": "已将云知识库指针写入 {{path}}",
"imaAuthTitle": "官方 IMA Agent Interface",
"imaAuthHint": "打开 IMA 配置页生成 Client ID 与 API Key(密钥只展示一次),粘贴到这里。FreeOS 只使用官方 ima-openapi 请求头。",
"imaAuthLink": "打开 ima.qq.com/agent-interface",
"imaClientId": "Client ID",
"imaApiKey": "API Key",
"imaClientIdRequired": "请输入 IMA Client ID",
"imaApiKeyRequired": "请输入 IMA API Key",
"imaConnectAction": "连接 ima",
"imaReconnectAction": "使用此连接",
"imaConnected": "已连接 ima({{preview}})",
"imaChangeCredentials": "更换凭证",
"imaExistingConnection": "已有 IMA 连接",
"imaExistingPlaceholder": "选择已保存的 IMA 连接",
"imaSearchBases": "搜索 ima 知识库",
"imaSearchDocs": "在当前知识库中搜索文档",
"imaDocsSearchEmpty": "没有匹配的文档。",
"imaSelectBases": "选择知识库",
"imaSelectDocs": "选择文档",
"imaBasesEmpty": "还没有 ima 知识库。请先在 ima 中创建,然后刷新。",
"imaDocsEmpty": "此文件夹没有文档。",
"imaLoadBasesFailed": "无法加载 ima 知识库",
"imaLoadDocsFailed": "无法加载 ima 文档",
"imaSelectionRequired": "请至少选择一个知识库或文档",
"imaSelectionSummary": "已选 {{bases}} 个知识库、{{docs}} 个文档",
"imaWholeBaseHint": "该知识库未勾选文档,将接入整个知识库。",
"imaRefresh": "刷新",
"imaLoadMore": "加载更多",
"imaFolder": "文件夹",
"imaRootFolder": "根目录",
"imaSelectAllDocs": "全选本页文档",
"imaClearDocs": "清除文档选择",
"backToList": "返回列表",
"createdBy": "创建者:{{name}}",
"baseId": "知识库 ID",
Expand Down
Loading
Loading