Remove env fallback for model choices
This commit is contained in:
@@ -17,10 +17,6 @@ OPENAI_API_KEY=
|
|||||||
# Optional: set a default model. Leave empty to use the Codex runtime default.
|
# Optional: set a default model. Leave empty to use the Codex runtime default.
|
||||||
CODEX_DEFAULT_MODEL=
|
CODEX_DEFAULT_MODEL=
|
||||||
|
|
||||||
# Optional: fallback model buttons for /model if the live Codex model list is unavailable.
|
|
||||||
# Format: "Label=model,Label=model"
|
|
||||||
CODEX_MODEL_CHOICES=GPT-5.5=gpt-5.5,GPT-5.4=gpt-5.4,GPT-5.4 mini=gpt-5.4-mini
|
|
||||||
|
|
||||||
# Optional: full-access, workspace-write, or read-only. Server-admin mode defaults to full-access.
|
# Optional: full-access, workspace-write, or read-only. Server-admin mode defaults to full-access.
|
||||||
CODEX_SANDBOX=full-access
|
CODEX_SANDBOX=full-access
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ journalctl -u codex-tg-bot -f
|
|||||||
| `WORK_DIR` | Рабочая директория Codex, по умолчанию `/` |
|
| `WORK_DIR` | Рабочая директория Codex, по умолчанию `/` |
|
||||||
| `OPENAI_API_KEY` | Опционально; если пусто, SDK переиспользует существующий Codex login |
|
| `OPENAI_API_KEY` | Опционально; если пусто, SDK переиспользует существующий Codex login |
|
||||||
| `CODEX_DEFAULT_MODEL` | Опциональная модель по умолчанию |
|
| `CODEX_DEFAULT_MODEL` | Опциональная модель по умолчанию |
|
||||||
| `CODEX_MODEL_CHOICES` | Fallback-кнопки `/model`, если live-список Codex недоступен; формат `Label=model,Label=model` |
|
|
||||||
| `CODEX_SANDBOX` | `full-access`, `workspace-write` или `read-only`; по умолчанию `full-access` |
|
| `CODEX_SANDBOX` | `full-access`, `workspace-write` или `read-only`; по умолчанию `full-access` |
|
||||||
| `CODEX_APPROVAL_MODE` | `auto_review` или `deny_all`; по умолчанию `auto_review` |
|
| `CODEX_APPROVAL_MODE` | `auto_review` или `deny_all`; по умолчанию `auto_review` |
|
||||||
| `CODEX_BIN` | Опциональный путь к конкретному `codex` executable |
|
| `CODEX_BIN` | Опциональный путь к конкретному `codex` executable |
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ class ModelChoice:
|
|||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ModelOptions:
|
class ModelOptions:
|
||||||
models: tuple[ModelChoice, ...]
|
models: tuple[ModelChoice, ...]
|
||||||
fallback: bool = False
|
|
||||||
error: str | None = None
|
error: str | None = None
|
||||||
|
|
||||||
def by_value(self, value: str | None) -> ModelChoice | None:
|
def by_value(self, value: str | None) -> ModelChoice | None:
|
||||||
@@ -208,15 +207,6 @@ def _codex_config() -> CodexConfig:
|
|||||||
return CodexConfig(codex_bin=config.CODEX_BIN)
|
return CodexConfig(codex_bin=config.CODEX_BIN)
|
||||||
|
|
||||||
|
|
||||||
def _fallback_model_options(error: str | None = None) -> ModelOptions:
|
|
||||||
all_efforts = tuple(effort.value for effort in ReasoningEffort)
|
|
||||||
models = tuple(
|
|
||||||
ModelChoice(label=label, value=value, efforts=all_efforts)
|
|
||||||
for label, value in config.MODEL_CHOICES
|
|
||||||
)
|
|
||||||
return ModelOptions(models=models, fallback=True, error=error)
|
|
||||||
|
|
||||||
|
|
||||||
def _reasoning_efforts(raw_options: Any) -> tuple[str, ...]:
|
def _reasoning_efforts(raw_options: Any) -> tuple[str, ...]:
|
||||||
efforts: list[str] = []
|
efforts: list[str] = []
|
||||||
for option in raw_options or []:
|
for option in raw_options or []:
|
||||||
@@ -235,7 +225,7 @@ async def list_model_options() -> ModelOptions:
|
|||||||
|
|
||||||
response = await codex.models()
|
response = await codex.models()
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return _fallback_model_options(error=str(exc))
|
return ModelOptions(models=(), error=str(exc))
|
||||||
|
|
||||||
models: list[ModelChoice] = []
|
models: list[ModelChoice] = []
|
||||||
for model in response.data:
|
for model in response.data:
|
||||||
@@ -251,7 +241,7 @@ async def list_model_options() -> ModelOptions:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not models:
|
if not models:
|
||||||
return _fallback_model_options(error="Codex returned no visible models")
|
return ModelOptions(models=(), error="Codex returned no visible models")
|
||||||
|
|
||||||
return ModelOptions(models=tuple(models))
|
return ModelOptions(models=tuple(models))
|
||||||
|
|
||||||
|
|||||||
@@ -37,28 +37,3 @@ SYSTEM_PROMPT = os.environ.get(
|
|||||||
"Execute the tasks you are given carefully. Answer concisely: the reply is shown "
|
"Execute the tasks you are given carefully. Answer concisely: the reply is shown "
|
||||||
"in a Telegram chat. Reply in the same language the user writes in.",
|
"in a Telegram chat. Reply in the same language the user writes in.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _parse_model_choices(raw: str) -> list[tuple[str, str]]:
|
|
||||||
choices: list[tuple[str, str]] = []
|
|
||||||
for item in raw.split(","):
|
|
||||||
item = item.strip()
|
|
||||||
if not item:
|
|
||||||
continue
|
|
||||||
if "=" in item:
|
|
||||||
label, value = item.split("=", 1)
|
|
||||||
else:
|
|
||||||
label = value = item
|
|
||||||
label = label.strip()
|
|
||||||
value = value.strip()
|
|
||||||
if label and value:
|
|
||||||
choices.append((label, value))
|
|
||||||
return choices
|
|
||||||
|
|
||||||
|
|
||||||
MODEL_CHOICES = _parse_model_choices(
|
|
||||||
os.environ.get(
|
|
||||||
"CODEX_MODEL_CHOICES",
|
|
||||||
"GPT-5.5=gpt-5.5,GPT-5.4=gpt-5.4,GPT-5.4 mini=gpt-5.4-mini",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|||||||
14
bot/main.py
14
bot/main.py
@@ -174,11 +174,14 @@ async def cmd_compact(_, message: Message):
|
|||||||
async def cmd_model(_, message: Message):
|
async def cmd_model(_, message: Message):
|
||||||
loading = await message.reply_text("Загружаю список моделей Codex...")
|
loading = await message.reply_text("Загружаю список моделей Codex...")
|
||||||
options = await _model_options()
|
options = await _model_options()
|
||||||
text = MODEL_TEXT
|
if options.error or not options.models:
|
||||||
if options.fallback:
|
await loading.edit_text(
|
||||||
text += "\n\nНе удалось получить live-список, показываю fallback из env."
|
"Не удалось загрузить список моделей из Codex. "
|
||||||
|
"Проверьте авторизацию и логи бота."
|
||||||
|
)
|
||||||
|
return
|
||||||
await loading.edit_text(
|
await loading.edit_text(
|
||||||
text,
|
MODEL_TEXT,
|
||||||
parse_mode=ParseMode.MARKDOWN,
|
parse_mode=ParseMode.MARKDOWN,
|
||||||
reply_markup=_model_keyboard(message.chat.id, options),
|
reply_markup=_model_keyboard(message.chat.id, options),
|
||||||
)
|
)
|
||||||
@@ -198,6 +201,9 @@ async def on_settings_callback(_, query: CallbackQuery):
|
|||||||
value = raw or None
|
value = raw or None
|
||||||
chat_id = query.message.chat.id
|
chat_id = query.message.chat.id
|
||||||
options = await _model_options()
|
options = await _model_options()
|
||||||
|
if options.error or not options.models:
|
||||||
|
log.warning("Model options reload failed: %s", options.error)
|
||||||
|
return
|
||||||
chats.set(chat_id, key, value)
|
chats.set(chat_id, key, value)
|
||||||
if key == "model":
|
if key == "model":
|
||||||
current_effort = chats.get(chat_id, "effort")
|
current_effort = chats.get(chat_id, "effort")
|
||||||
|
|||||||
Reference in New Issue
Block a user