1
This commit is contained in:
@@ -59,6 +59,63 @@ func LocalAiClient(ctx context.Context) *LocalAi {
|
||||
}
|
||||
}
|
||||
|
||||
// QwenVL 调 local-ai 的多模态 VLM(qwen3.8-9b + mmproj,llama.cpp 后端)做图片推理。
|
||||
// 注意:VL 与 z-image 生成不能同时驻留 12G 显存(OOM),调用方须保证阶段互斥。
|
||||
func QwenVL(ctx context.Context, imgData []byte, mime, prompt string) (string, error) {
|
||||
base := strings.TrimRight(g.Cfg().MustGet(ctx, "localAi.baseUrl").String(), "/")
|
||||
if base == "" {
|
||||
return "", fmt.Errorf("localAi.baseUrl 未配置")
|
||||
}
|
||||
model := g.Cfg().MustGet(ctx, "localAi.vlmModel", "qwen3.8-9b").String()
|
||||
b64 := base64.StdEncoding.EncodeToString(imgData)
|
||||
body, err := json.Marshal(map[string]any{
|
||||
"model": model,
|
||||
"messages": []map[string]any{{
|
||||
"role": "user",
|
||||
"content": []map[string]any{
|
||||
{"type": "image_url", "image_url": map[string]string{"url": fmt.Sprintf("data:%s;base64,%s", mime, b64)}},
|
||||
{"type": "text", "text": prompt},
|
||||
},
|
||||
}},
|
||||
"max_tokens": 600,
|
||||
"temperature": 0.3,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, base+"/v1/chat/completions", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
raw, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("VLM HTTP %d: %s", resp.StatusCode, truncateStr(string(raw), 200))
|
||||
}
|
||||
var out struct {
|
||||
Choices []struct {
|
||||
Message struct {
|
||||
Content string `json:"content"`
|
||||
} `json:"message"`
|
||||
} `json:"choices"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &out); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(out.Choices) == 0 {
|
||||
return "", fmt.Errorf("VLM 无响应内容")
|
||||
}
|
||||
return out.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
// Detect 对单张图片做全图检测(不做任何裁剪,位置由模型自行推理):
|
||||
// 整图等比缩放至最长边 InputSize 提交,坐标映射回原图像素后返回。
|
||||
// imgW/imgH 为原图尺寸;返回坐标均为原图像素尺度。
|
||||
|
||||
Reference in New Issue
Block a user