forked from WHC/Hermit
providers: add teamorouter; pick the first /models id when no default model
This commit is contained in:
+16
-1
@@ -20,7 +20,8 @@
|
||||
static void usage(const char *argv0) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s [options] (works in the current directory)\n"
|
||||
" -P, --provider NAME minimax | deepseek | openai (last used)\n"
|
||||
" -P, --provider NAME minimax | deepseek | openai | teamorouter\n"
|
||||
" (last used)\n"
|
||||
" -m, --model NAME model (last used for the provider)\n"
|
||||
" -e, --effort LEVEL low | medium | high | max (last used)\n"
|
||||
" -w, --workspace DIR project directory (default: current)\n"
|
||||
@@ -539,6 +540,20 @@ int main(int argc, char **argv) {
|
||||
curl_global_cleanup();
|
||||
return 1;
|
||||
}
|
||||
/* Router presets have no fixed model: take the first one the API lists
|
||||
* (the TUI does the same after its /models probe). */
|
||||
if (!cfg.model[0] && cfg.headless) {
|
||||
char ids[1][128], err[256] = "";
|
||||
if (cst_llm_list_models(cfg.api_base, cfg.api_key, ids, 1, err,
|
||||
sizeof(err)) > 0) {
|
||||
snprintf(cfg.model, sizeof(cfg.model), "%s", ids[0]);
|
||||
} else {
|
||||
fprintf(stderr, "no model for %s: pass --model (%s)\n", cfg.provider,
|
||||
err[0] ? err : "the API listed none");
|
||||
curl_global_cleanup();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"using %s model=%s effort=%s protocol=%s tools=%s max_steps=%d\n",
|
||||
|
||||
@@ -11,6 +11,9 @@ static const cst_provider_t PROVIDERS[] = {
|
||||
{"deepseek", "https://api.deepseek.com/v1", "deepseek-flash",
|
||||
"DEEPSEEK_API_KEY"},
|
||||
{"openai", "https://api.openai.com/v1", "gpt-4.1-mini", "OPENAI_API_KEY"},
|
||||
/* Router: no fixed model; the first id from GET /models is picked. */
|
||||
{"teamorouter", "https://api.teamorouter.com/v1", "",
|
||||
"TEAMOROUTER_API_KEY"},
|
||||
};
|
||||
|
||||
const cst_provider_t *cst_provider_find(const char *name) {
|
||||
@@ -102,6 +105,10 @@ long cst_model_context_tokens(const char *model) {
|
||||
{"o3", 200000}, {"o4", 200000}, {"claude", 200000},
|
||||
{"qwen", 131072}, {"glm", 128000}, {"kimi", 131072}};
|
||||
if (model) {
|
||||
/* routers name models "vendor/model" */
|
||||
const char *slash = strrchr(model, '/');
|
||||
if (slash && slash[1])
|
||||
model = slash + 1;
|
||||
for (size_t i = 0; i < sizeof(T) / sizeof(T[0]); i++) {
|
||||
if (strncmp(model, T[i].prefix, strlen(T[i].prefix)) == 0)
|
||||
return T[i].tokens;
|
||||
|
||||
@@ -3821,7 +3821,7 @@ static int secret_input_run(tui_t *t, const char *title, const char *hint,
|
||||
* File-scope so a probe still running when the TUI exits writes nowhere
|
||||
* stale. */
|
||||
|
||||
#define AM_MAX 64
|
||||
#define AM_MAX 512 /* routers list hundreds; the picker filters as you type */
|
||||
enum { AM_NONE = 0, AM_RUNNING, AM_OK, AM_FAILED, AM_AUTH };
|
||||
|
||||
static pthread_mutex_t g_am_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
@@ -3920,15 +3920,23 @@ static int am_models(const char *provider, char out[][128], int max) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/* After a probe: flag a model the API does not list. */
|
||||
/* After a probe: flag a model the API does not list; with no model yet
|
||||
* (router presets) take the first one listed. */
|
||||
static void am_check_model(tui_t *t) {
|
||||
const cst_agent_config_t *cfg = cst_agent_config(t->agent);
|
||||
cst_agent_config_t *cfg = cst_agent_config_mut(t->agent);
|
||||
char msg[400] = "";
|
||||
int picked = 0;
|
||||
pthread_mutex_lock(&g_am_mu);
|
||||
if (cfg && strcmp(g_am.provider, cfg->provider) == 0) {
|
||||
if (g_am.state == AM_AUTH) {
|
||||
snprintf(msg, sizeof(msg), "the %s API key was rejected — /key to "
|
||||
"replace it", cfg->provider);
|
||||
} else if (g_am.state == AM_OK && g_am.n > 0 && !cfg->model[0]) {
|
||||
snprintf(cfg->model, sizeof(cfg->model), "%s", g_am.ids[0]);
|
||||
snprintf(msg, sizeof(msg), "model → %.127s (%d listed by %s) — /model "
|
||||
"to change",
|
||||
cfg->model, g_am.n, cfg->provider);
|
||||
picked = 1;
|
||||
} else if (g_am.state == AM_OK && g_am.n > 0) {
|
||||
int found = 0;
|
||||
for (int i = 0; i < g_am.n && !found; i++)
|
||||
@@ -3939,6 +3947,8 @@ static void am_check_model(tui_t *t) {
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_am_mu);
|
||||
if (picked)
|
||||
cst_settings_remember(cfg);
|
||||
if (msg[0])
|
||||
set_status(t, msg);
|
||||
}
|
||||
@@ -4314,7 +4324,8 @@ static void cmd_provider(tui_t *t, char *arg, int *quit) {
|
||||
int cur_idx = 0;
|
||||
for (int i = 0; i < n && i < 16; i++) {
|
||||
const cst_provider_t *q = cst_provider_at(i);
|
||||
snprintf(items[i], 128, "%-9s %s (%s)", q->name, q->api_base, q->model);
|
||||
snprintf(items[i], 128, "%-11s %s (%s)", q->name, q->api_base,
|
||||
q->model[0] ? q->model : "models from API");
|
||||
if (strcmp(q->name, cfg->provider) == 0)
|
||||
cur_idx = i;
|
||||
}
|
||||
@@ -5409,6 +5420,12 @@ static void request_cancel_ui(tui_t *t) {
|
||||
|
||||
static int begin_turn(tui_t *t, char *prompt) {
|
||||
prompt_hist_push(t, prompt);
|
||||
if (!cst_agent_config(t->agent)->model[0]) {
|
||||
set_status(t, "no model selected yet — /model to pick one (↑ recalls "
|
||||
"your message)");
|
||||
free(prompt);
|
||||
return -1;
|
||||
}
|
||||
block_t *ub = blocks_push(&t->hist);
|
||||
if (ub) {
|
||||
ub->kind = BLK_USER;
|
||||
|
||||
Reference in New Issue
Block a user