@honlnk/picsense 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +235 -0
- package/dist/core/image-loader.js +123 -0
- package/dist/core/image-loader.js.map +1 -0
- package/dist/core/session-manager.js +231 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/video-loader.js +260 -0
- package/dist/core/video-loader.js.map +1 -0
- package/dist/index.js +177 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/index.js +31 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.js +185 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/registry.js +15 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/retry.js +51 -0
- package/dist/providers/retry.js.map +1 -0
- package/dist/tools/analyze-document.js +130 -0
- package/dist/tools/analyze-document.js.map +1 -0
- package/dist/tools/analyze-images.js +37 -0
- package/dist/tools/analyze-images.js.map +1 -0
- package/dist/tools/analyze-video.js +46 -0
- package/dist/tools/analyze-video.js.map +1 -0
- package/dist/tools/list-sessions.js +11 -0
- package/dist/tools/list-sessions.js.map +1 -0
- package/dist/utils/config.js +88 -0
- package/dist/utils/config.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* analyze_images 工具业务逻辑。
|
|
3
|
+
*
|
|
4
|
+
* 设计依据:docs/picsense-design.md §4.1、§4.2.5。
|
|
5
|
+
* - 输入 image_sources(URL/路径/base64 自动识别)+ prompt + 可选 session_id。
|
|
6
|
+
* - 无 session_id → 创建 session;有 → 追加一轮。
|
|
7
|
+
* - 返回 { session_id, summary, description }。
|
|
8
|
+
*
|
|
9
|
+
* 本函数是纯业务,不涉及 MCP 注册(那在 P5)。
|
|
10
|
+
*/
|
|
11
|
+
export class ToolInputError extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'ToolInputError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 执行图片识别(支持多轮迭代)。
|
|
19
|
+
*
|
|
20
|
+
* 注意:首次调用时 image_sources 必填;传入已有 session_id 时,
|
|
21
|
+
* 沿用该 session 的图片,image_sources 会被忽略。
|
|
22
|
+
*/
|
|
23
|
+
export async function analyzeImages(manager, params) {
|
|
24
|
+
if (typeof params.prompt !== 'string' || params.prompt.trim() === '') {
|
|
25
|
+
throw new ToolInputError('prompt is required and must be a non-empty string.');
|
|
26
|
+
}
|
|
27
|
+
// 后续轮次(带 session_id)时,图片已在 session 内,不需要再传。
|
|
28
|
+
if (params.session_id) {
|
|
29
|
+
return manager.appendTurn(params.session_id, params.prompt);
|
|
30
|
+
}
|
|
31
|
+
// 首轮:必须有图片。
|
|
32
|
+
if (!Array.isArray(params.image_sources) || params.image_sources.length === 0) {
|
|
33
|
+
throw new ToolInputError('image_sources is required (with at least one image) when creating a new session.');
|
|
34
|
+
}
|
|
35
|
+
return manager.createSession(params.image_sources, params.prompt);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=analyze-images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-images.js","sourceRoot":"","sources":["../../src/tools/analyze-images.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgBH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAuB,EACvB,MAA2B;IAE3B,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrE,MAAM,IAAI,cAAc,CAAC,oDAAoD,CAAC,CAAC;IACjF,CAAC;IAED,4CAA4C;IAC5C,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED,YAAY;IACZ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,cAAc,CACtB,kFAAkF,CACnF,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* analyze_video 工具业务逻辑。
|
|
3
|
+
*
|
|
4
|
+
* 设计依据:docs/picsense-design.md §4.1、§4.3。
|
|
5
|
+
*
|
|
6
|
+
* 视频是独立输入形态,与图片并列。由于项目默认 provider(OpenAI Responses API)
|
|
7
|
+
* 原生不支持视频,本工具通过抽帧把视频转成帧序列,复用图片管线发送给视觉模型。
|
|
8
|
+
*
|
|
9
|
+
* - 输入 video_source(URL/本地路径)+ prompt + 可选 session_id。
|
|
10
|
+
* - 无 session_id → 抽帧创建 session;有 → 追加一轮(复用现有 appendTurn)。
|
|
11
|
+
* - 返回 { session_id, summary, description },与 analyze_images 一致。
|
|
12
|
+
*
|
|
13
|
+
* 抽帧在首轮一次性完成,后续轮次复用 session(帧作为首轮图片已挂载),
|
|
14
|
+
* 不会重复抽帧——这与 SessionManager 的设计一致(图片只挂首轮)。
|
|
15
|
+
*/
|
|
16
|
+
import { loadVideo, VideoLoadError } from '../core/video-loader.js';
|
|
17
|
+
// 复用 analyze-images 的输入校验错误类型,保持一致语义。
|
|
18
|
+
import { ToolInputError } from './analyze-images.js';
|
|
19
|
+
export { VideoLoadError };
|
|
20
|
+
/**
|
|
21
|
+
* 执行视频识别(支持多轮迭代)。
|
|
22
|
+
*
|
|
23
|
+
* @param manager Session 管理器。
|
|
24
|
+
* @param params 工具参数。
|
|
25
|
+
* @param maxVideoBytes 单个视频字节上限。
|
|
26
|
+
* @param frameOpts 抽帧选项(fps / maxFrames)。
|
|
27
|
+
*/
|
|
28
|
+
export async function analyzeVideo(manager, params, maxVideoBytes, frameOpts) {
|
|
29
|
+
if (typeof params.prompt !== 'string' || params.prompt.trim() === '') {
|
|
30
|
+
throw new ToolInputError('prompt is required and must be a non-empty string.');
|
|
31
|
+
}
|
|
32
|
+
// 后续轮次(带 session_id)时,视频帧已在 session 内,无需再抽帧。
|
|
33
|
+
if (params.session_id) {
|
|
34
|
+
return manager.appendTurn(params.session_id, params.prompt);
|
|
35
|
+
}
|
|
36
|
+
// 首轮:必须有视频源。
|
|
37
|
+
if (typeof params.video_source !== 'string' || params.video_source.trim() === '') {
|
|
38
|
+
throw new ToolInputError('video_source is required when creating a new session.');
|
|
39
|
+
}
|
|
40
|
+
// 抽帧。
|
|
41
|
+
const video = await loadVideo(params.video_source, maxVideoBytes, frameOpts);
|
|
42
|
+
// 用抽出的帧创建 session(复用 createSessionWithImages,帧已加载无需再加载)。
|
|
43
|
+
// displaySources 用原始视频引用(URL/路径),避免巨大的 base64 占用 list_sessions 展示。
|
|
44
|
+
return manager.createSessionWithImages(video.frames, [video.source], params.prompt);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=analyze-video.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-video.js","sourceRoot":"","sources":["../../src/tools/analyze-video.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,SAAS,EAAqB,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACvF,sCAAsC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAcrD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAuB,EACvB,MAA0B,EAC1B,aAAqB,EACrB,SAAuB;IAEvB,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrE,MAAM,IAAI,cAAc,CAAC,oDAAoD,CAAC,CAAC;IACjF,CAAC;IAED,6CAA6C;IAC7C,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED,aAAa;IACb,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACjF,MAAM,IAAI,cAAc,CAAC,uDAAuD,CAAC,CAAC;IACpF,CAAC;IAED,MAAM;IACN,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,YAAY,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IAE7E,yDAAyD;IACzD,mEAAmE;IACnE,OAAO,OAAO,CAAC,uBAAuB,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* list_sessions 工具业务逻辑。
|
|
3
|
+
*
|
|
4
|
+
* 设计依据:docs/picsense-design.md §4.1、§4.2.5。
|
|
5
|
+
* 返回当前所有 session 的列表(含简介、关联图片、迭代轮数、最后访问时间)。
|
|
6
|
+
* 截断显示已由 SessionManager.toSnapshot 完成。
|
|
7
|
+
*/
|
|
8
|
+
export async function listSessions(manager) {
|
|
9
|
+
return { sessions: manager.listSessions() };
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=list-sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-sessions.js","sourceRoot":"","sources":["../../src/tools/list-sessions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAuB;IACxD,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 环境变量配置管理。
|
|
3
|
+
*
|
|
4
|
+
* 设计依据:docs/picsense-design.md §6.2 —— 全部配置走环境变量,代码内零硬编码。
|
|
5
|
+
* 缺失必填项时抛出清晰错误,便于用户在 stdio 启动时第一时间发现问题。
|
|
6
|
+
*/
|
|
7
|
+
const ONE_MB = 1024 * 1024;
|
|
8
|
+
const ONE_HOUR = 60 * 60 * 1000;
|
|
9
|
+
const SUPPORTED_PROVIDERS = ['openai', 'qwen', 'kimi'];
|
|
10
|
+
function isProviderName(value) {
|
|
11
|
+
return SUPPORTED_PROVIDERS.includes(value);
|
|
12
|
+
}
|
|
13
|
+
function readProvider(name, env) {
|
|
14
|
+
const prefix = name.toUpperCase();
|
|
15
|
+
const apiKey = env[`${prefix}_API_KEY`];
|
|
16
|
+
const model = env[`${prefix}_MODEL`];
|
|
17
|
+
const baseUrl = env[`${prefix}_BASE_URL`];
|
|
18
|
+
// 没 Key 视为该 provider 未启用。
|
|
19
|
+
if (!apiKey)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (!model) {
|
|
22
|
+
throw new ConfigError(`${prefix}_MODEL is required when ${prefix}_API_KEY is set.`);
|
|
23
|
+
}
|
|
24
|
+
return { name, apiKey, model, baseUrl };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 加载并校验配置。必填项缺失时抛 ConfigError。
|
|
28
|
+
*
|
|
29
|
+
* @param env 可注入环境变量,默认读 process.env,便于测试。
|
|
30
|
+
*/
|
|
31
|
+
export function loadConfig(env = process.env) {
|
|
32
|
+
const defaultProviderRaw = env['DEFAULT_PROVIDER'] ?? 'openai';
|
|
33
|
+
if (!isProviderName(defaultProviderRaw)) {
|
|
34
|
+
throw new ConfigError(`DEFAULT_PROVIDER="${defaultProviderRaw}" is not supported. Supported: ${SUPPORTED_PROVIDERS.join(', ')}.`);
|
|
35
|
+
}
|
|
36
|
+
const defaultProvider = defaultProviderRaw;
|
|
37
|
+
const providers = {
|
|
38
|
+
openai: readProvider('openai', env),
|
|
39
|
+
qwen: readProvider('qwen', env),
|
|
40
|
+
kimi: readProvider('kimi', env),
|
|
41
|
+
};
|
|
42
|
+
// 默认 provider 必须可用(已配置 Key)。
|
|
43
|
+
if (!providers[defaultProvider]) {
|
|
44
|
+
const prefix = defaultProvider.toUpperCase();
|
|
45
|
+
throw new ConfigError(`Default provider "${defaultProvider}" is not configured. ` +
|
|
46
|
+
`Please set ${prefix}_API_KEY and ${prefix}_MODEL (see .env.example).`);
|
|
47
|
+
}
|
|
48
|
+
const maxImageMb = Number(env['MAX_IMAGE_MB'] ?? '5');
|
|
49
|
+
if (!Number.isFinite(maxImageMb) || maxImageMb <= 0) {
|
|
50
|
+
throw new ConfigError(`MAX_IMAGE_MB="${env['MAX_IMAGE_MB']}" is not a positive number.`);
|
|
51
|
+
}
|
|
52
|
+
const timeoutMs = Number(env['TIMEOUT_MS'] ?? '300000');
|
|
53
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
54
|
+
throw new ConfigError(`TIMEOUT_MS="${env['TIMEOUT_MS']}" is not a positive number.`);
|
|
55
|
+
}
|
|
56
|
+
// 视频配置(可选,均有默认值)。
|
|
57
|
+
const maxVideoMb = Number(env['MAX_VIDEO_MB'] ?? '100');
|
|
58
|
+
if (!Number.isFinite(maxVideoMb) || maxVideoMb <= 0) {
|
|
59
|
+
throw new ConfigError(`MAX_VIDEO_MB="${env['MAX_VIDEO_MB']}" is not a positive number.`);
|
|
60
|
+
}
|
|
61
|
+
const videoMaxFrames = Number(env['VIDEO_MAX_FRAMES'] ?? '30');
|
|
62
|
+
if (!Number.isFinite(videoMaxFrames) || videoMaxFrames <= 0) {
|
|
63
|
+
throw new ConfigError(`VIDEO_MAX_FRAMES="${env['VIDEO_MAX_FRAMES']}" is not a positive number.`);
|
|
64
|
+
}
|
|
65
|
+
const videoFps = Number(env['VIDEO_FPS'] ?? '1');
|
|
66
|
+
if (!Number.isFinite(videoFps) || videoFps <= 0) {
|
|
67
|
+
throw new ConfigError(`VIDEO_FPS="${env['VIDEO_FPS']}" is not a positive number.`);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
defaultProvider,
|
|
71
|
+
providers,
|
|
72
|
+
maxImageBytes: Math.floor(maxImageMb * ONE_MB),
|
|
73
|
+
maxVideoBytes: Math.floor(maxVideoMb * ONE_MB),
|
|
74
|
+
videoMaxFrames: Math.floor(videoMaxFrames),
|
|
75
|
+
videoFps,
|
|
76
|
+
timeoutMs,
|
|
77
|
+
sessionTtlMs: 24 * ONE_HOUR,
|
|
78
|
+
sessionCleanupIntervalMs: ONE_HOUR,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** 配置错误,携带面向用户的提示信息。 */
|
|
82
|
+
export class ConfigError extends Error {
|
|
83
|
+
constructor(message) {
|
|
84
|
+
super(message);
|
|
85
|
+
this.name = 'ConfigError';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@honlnk/picsense",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A local MCP server that gives unimodal LLMs multimodal vision — image/video recognition with multi-turn iterative analysis.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"author": "honlnk",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/honlnk/picsense.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/honlnk/picsense#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/honlnk/picsense/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"mcp-server",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"claude",
|
|
21
|
+
"vision",
|
|
22
|
+
"image-recognition",
|
|
23
|
+
"video-recognition",
|
|
24
|
+
"multimodal",
|
|
25
|
+
"llm-tools",
|
|
26
|
+
"ai-tools"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20"
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"picsense": "dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', 0o755)\"",
|
|
42
|
+
"dev": "tsx watch src/index.ts",
|
|
43
|
+
"start": "node dist/index.js",
|
|
44
|
+
"smoke": "tsx scripts/smoke.ts",
|
|
45
|
+
"smoke:session": "tsx scripts/smoke-session.ts",
|
|
46
|
+
"smoke:tools": "tsx scripts/smoke-tools.ts",
|
|
47
|
+
"smoke:video": "tsx scripts/smoke-video.ts",
|
|
48
|
+
"e2e": "pnpm build && tsx scripts/e2e-client.ts",
|
|
49
|
+
"sim": "pnpm build && tsx scripts/zcode-sim.ts",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"prepublishOnly": "npm run build && npm run typecheck"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
55
|
+
"ffmpeg-static": "^5.3.0",
|
|
56
|
+
"zod": "^3.25.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "^22.0.0",
|
|
60
|
+
"tsx": "^4.19.0",
|
|
61
|
+
"typescript": "^5.6.0"
|
|
62
|
+
},
|
|
63
|
+
"pnpm": {
|
|
64
|
+
"onlyBuiltDependencies": ["ffmpeg-static"]
|
|
65
|
+
}
|
|
66
|
+
}
|