Skip to content
Open
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
10 changes: 10 additions & 0 deletions agent/app/provider/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down Expand Up @@ -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"},
Expand Down
4 changes: 2 additions & 2 deletions agent/app/provider/openclaw.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, "/")
Expand Down
4 changes: 2 additions & 2 deletions agent/app/provider/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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{}{
Expand Down
2 changes: 1 addition & 1 deletion agent/app/service/agents_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion agent/utils/terminal/ai/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion agent/utils/terminal/ai/config_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/utils/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/views/ai/agents/model/add/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down