mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
feat(ai): add llmman as a local model provider (#13717)
llmman (https://github.com/llmmanorg/llmman) is a local model runner serving Ollama- and OpenAI-compatible routes on 127.0.0.1:17434. Register it in the agent provider catalog next to Ollama and extend every Ollama special case (no API key, verification skipped, OpenClaw placeholder key, manual initial model) to cover it as well.
This commit is contained in:
@@ -46,6 +46,15 @@ var catalog = map[string]Meta{
|
||||
Key: "ollama", DisplayName: "Ollama", Sort: 15, DefaultAPIType: "openai-responses",
|
||||
APIConfigs: editableAPIConfigs(false, "openai-responses", "openai-completions", "openai-embeddings"),
|
||||
},
|
||||
// llmman (https://github.com/llmmanorg/llmman): local runner with Ollama/OpenAI-compatible routes on 127.0.0.1:17434.
|
||||
"llmman": {
|
||||
Key: "llmman", DisplayName: "llmman", Sort: 16, DefaultAPIType: "openai-responses",
|
||||
APIConfigs: []APIConfig{
|
||||
{APIType: "openai-responses", BaseURL: "http://127.0.0.1:17434/v1", EditableBaseURL: true},
|
||||
{APIType: "openai-completions", BaseURL: "http://127.0.0.1:17434/v1", EditableBaseURL: true},
|
||||
{APIType: "openai-embeddings", BaseURL: "http://127.0.0.1:17434/v1", EditableBaseURL: true},
|
||||
},
|
||||
},
|
||||
"vllm": {
|
||||
Key: "vllm", DisplayName: "vLLM", Sort: 20, DefaultAPIType: "openai-completions", EnvKey: "VLLM_API_KEY",
|
||||
APIConfigs: editableAPIConfigs(false, "openai-completions", "openai-responses", "anthropic-messages", "openai-images", "openai-embeddings"),
|
||||
@@ -453,6 +462,7 @@ var legacyModelPrefixes = map[string][]string{
|
||||
"custom": {"custom"},
|
||||
"vllm": {"custom"},
|
||||
"ollama": {"ollama"},
|
||||
"llmman": {"llmman"},
|
||||
"deepseek": {"deepseek"},
|
||||
"bailian-coding-plan": {"bailian-coding-plan"},
|
||||
"ark-coding-plan": {"ark-coding-plan"},
|
||||
|
||||
@@ -43,8 +43,8 @@ func BuildOpenClawProviderPatch(provider, modelName, apiType, authMode, baseURL,
|
||||
providerKey = "moonshot"
|
||||
resolvedAPIType = "openai-completions"
|
||||
usesBearer = false
|
||||
case "ollama":
|
||||
apiKey = "ollama"
|
||||
case "ollama", "llmman":
|
||||
apiKey = provider
|
||||
usesBearer = false
|
||||
case "openai", "openrouter", "anthropic":
|
||||
preserveQualifiedModel = strings.Contains(modelName, "/")
|
||||
|
||||
@@ -34,7 +34,7 @@ const (
|
||||
|
||||
func SkipVerification(provider string) bool {
|
||||
switch provider {
|
||||
case "vllm", "ollama", "kimi-coding":
|
||||
case "vllm", "ollama", "llmman", "kimi-coding":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -128,7 +128,7 @@ func BuildVerifyRequest(provider, apiType, authMode, baseURL, apiKey, model stri
|
||||
request.Body = mustJSON(map[string]interface{}{"model": model, "input": "test", "max_output_tokens": defaultVerifyMaxTokens, "stream": false})
|
||||
default:
|
||||
request.URL = baseURL + "/chat/completions"
|
||||
if provider != "ollama" || strings.TrimSpace(apiKey) != "" {
|
||||
if (provider != "ollama" && provider != "llmman") || strings.TrimSpace(apiKey) != "" {
|
||||
headers["Authorization"] = "Bearer " + apiKey
|
||||
}
|
||||
request.Body = mustJSON(map[string]interface{}{
|
||||
|
||||
@@ -1338,7 +1338,7 @@ func normalizeAgentAccountModel(account *model.AgentAccount, model dto.AgentAcco
|
||||
|
||||
func requiresInitialAgentAccountModels(provider string) bool {
|
||||
switch provider {
|
||||
case "custom", "vllm", "ollama":
|
||||
case "custom", "vllm", "ollama", "llmman":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
||||
@@ -92,7 +92,7 @@ func NewClient(cfg ClientConfig) (Client, error) {
|
||||
if strings.TrimSpace(cfg.Model) == "" {
|
||||
return nil, fmt.Errorf("model is required")
|
||||
}
|
||||
if strings.TrimSpace(cfg.APIKey) == "" && providerKey != "ollama" {
|
||||
if strings.TrimSpace(cfg.APIKey) == "" && providerKey != "ollama" && providerKey != "llmman" {
|
||||
return nil, fmt.Errorf("api key is required")
|
||||
}
|
||||
baseURL := normalizeBaseURL(providerKey, cfg.BaseURL)
|
||||
|
||||
@@ -68,7 +68,7 @@ func ResolveGeneratorConfig(accountID uint) (GeneratorConfig, time.Duration, err
|
||||
if apiKey == "" {
|
||||
apiKey = lookupProviderAPIKey(provider)
|
||||
}
|
||||
if apiKey == "" && provider != "ollama" {
|
||||
if apiKey == "" && provider != "ollama" && provider != "llmman" {
|
||||
return GeneratorConfig{}, 0, fmt.Errorf("agent account api key is required")
|
||||
}
|
||||
return GeneratorConfig{
|
||||
|
||||
@@ -16,7 +16,7 @@ export const getAgentProviderDisplayName = (provider: string, displayName?: stri
|
||||
};
|
||||
|
||||
export const isAgentAccountVerificationSkipped = (provider?: string): boolean =>
|
||||
['vllm', 'ollama', 'kimi-coding'].includes((provider || '').toLowerCase());
|
||||
['vllm', 'ollama', 'llmman', 'kimi-coding'].includes((provider || '').toLowerCase());
|
||||
|
||||
export const supportsAgentModelConfig = (agentType: string): boolean => {
|
||||
return agentType === 'openclaw' || agentType === 'hermes-agent';
|
||||
|
||||
@@ -271,7 +271,7 @@ const manualVerifyIndex = ref(-1);
|
||||
const editModels = ref<AI.AgentAccountModel[]>([]);
|
||||
const modelConfigMode = ref<'discover' | 'manual'>('discover');
|
||||
const modelDiscoveryFailed = ref(false);
|
||||
const initialModelProviders = ['custom', 'vllm', 'ollama'];
|
||||
const initialModelProviders = ['custom', 'vllm', 'ollama', 'llmman'];
|
||||
const { isIntl } = useGlobalStore();
|
||||
|
||||
const form = reactive({
|
||||
|
||||
Reference in New Issue
Block a user