Files
ligent/src/capability.h
T
2026-10-03 14:13:21 +08:00

65 lines
3.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* capability.h - 模型能力探测(vision / tools)
*
* 为什么需要它:OpenAI 兼容层**没有任何查询能力的接口**。DeepSeek 和 MiMo 的
* GET /v1/models 都只返回 id / object / owned_by,没有 capability / modality
* 字段。所以"这个模型收不收图片"只能靠**发一个试探请求**问出来。
*
* 这里叫"序列"是因为它不是一次请求,而是有先后的几步:
*
* 1. 基线:一句纯文本 + max_tokens=1
* └ 失败 → 什么都别下结论(可能是 key、网络、模型名的问题)
* └ 成功 → 说明模型是活的,后面的否定结论才有意义
* 2. 合并试探:图片 + tools 一起发
* └ 200 → 两样都支持(常见情况,到此为止,只花了 2 个请求)
* └ 非 200 → 拆开单独试,才知道是哪一个不行
* 3. 单独试探:图片、tools 各发一次
*
* 关键在于**否定结论只在基线成功时才敢下**:单个试探 400 而基线 200,说明
* 差别就在那一样东西上。这样不需要去猜服务端的错误文案("不支持图片"、
* "invalid content"、"multimodal not supported"…… 各家写法都不一样),
* 也就不会因为文案变化而误判。
*
* 猜不准的时候一律按"支持"处理:多暴露一个工具,代价是模型可能白调一次
* (我们还有 4xx 降级兜底);而错误地藏掉工具,用户会以为模型看不见图。
*
* 结果按 base_url + model 缓存,所以**换模型会自动重新测**,切回来则命中缓存。
*/
#ifndef LIGENT_CAPABILITY_H
#define LIGENT_CAPABILITY_H
#include "llm.h"
#include <stddef.h>
#define CAP_UNKNOWN (-1)
#define CAP_NO 0
#define CAP_YES 1
typedef struct {
int vision; /* 能不能收图片输入 */
int tools; /* 能不能收 tools 参数 */
int probed; /* 真的测过吗(0 = 没测或被禁用,结论一律按"支持") */
int conclusive; /* 基线成功了吗 —— 只有它成功,否定结论才作数 */
} Capabilities;
/* 探测(或命中缓存)。enable=0 时直接给"没测过"的全支持结果。
* verbose 会打印每个试探请求的细节。errbuf 收探测过程中的失败原因,
* 仅供提示,不影响结论。
* 返回 1 = 这次真的发了请求(调用方可以据此提示用户一次);
* 返回 0 = 命中缓存或被禁用(什么都没发生,别重复提示)。 */
int cap_get(const LlmConfig *cfg, int enable,
Capabilities *out, int verbose, char *errbuf, size_t errlen);
/* 只查缓存,不发任何请求。命中返回 1 并回填,没测过返回 0。
* 给 /tools 这种"想知道但绝不该为此发请求"的地方用。 */
int cap_cached(const LlmConfig *cfg, Capabilities *out);
/* 把已缓存的结果清掉(下次调用重新探测)。
* 正常换模型不需要它 —— 缓存按 base_url+model 索引;这个给测试用。 */
void cap_cache_clear(void);
/* 给人和日志看的中文说明,如"支持"/"不支持"/"未测"。字面量,不用释放 */
const char *cap_name(int v);
#endif /* LIGENT_CAPABILITY_H */