feat: add tool-call fallback and resilient batch exports
This commit is contained in:
1 parent
ab066733b8
commit
d31eb2a19d
32 files changed
+538
-72
No files matched your search
@@ -23,6 +23,13 @@ class ModelSettingsUpdate(BaseModel):
|
||||
timeout_seconds: float = Field(default=300, gt=0, le=3600)
|
||||
max_retries: int = Field(default=5, ge=0, le=10)
|
||||
retry_interval_seconds: float = Field(default=10, ge=0, le=300)
|
||||
rate_limit_retry_seconds: float | None = Field(default=None, ge=0, le=3600)
|
||||
tool_fallback_model: str | None = Field(default=None, min_length=1, max_length=200)
|
||||
tool_fallback_reasoning_effort: (
|
||||
Literal["default", "none", "minimal", "low", "medium", "high", "xhigh", "max"] | None
|
||||
) = None
|
||||
tool_fallback_max_retries: int | None = Field(default=None, ge=0, le=10)
|
||||
tool_fallback_no_progress_attempts: int | None = Field(default=None, ge=1, le=10)
|
||||
reasoning_effort: Literal[
|
||||
"default", "none", "minimal", "low", "medium", "high", "xhigh", "max"
|
||||
] = "high"
|
||||
@@ -45,9 +52,11 @@ class ModelSettingsUpdate(BaseModel):
|
||||
raise ValueError("请输入接口根地址,例如 https://api-n-cd.playones.com/v1")
|
||||
return value
|
||||
|
||||
@field_validator("model")
|
||||
@field_validator("model", "tool_fallback_model")
|
||||
@classmethod
|
||||
def validate_model(cls, value: str) -> str:
|
||||
def validate_model(cls, value: str | None) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
value = value.strip()
|
||||
if not value or any(c in value for c in "\r\n\x00"):
|
||||
raise ValueError("模型名称不能为空或包含换行")
|
||||
@@ -64,6 +73,11 @@ def get_model_settings() -> dict:
|
||||
"timeout_seconds": settings.OPENAI_TIMEOUT_SECONDS,
|
||||
"max_retries": settings.OPENAI_MAX_RETRIES,
|
||||
"retry_interval_seconds": settings.OPENAI_RETRY_INTERVAL_SECONDS,
|
||||
"rate_limit_retry_seconds": settings.OPENAI_RATE_LIMIT_RETRY_SECONDS,
|
||||
"tool_fallback_model": settings.OPENAI_TOOL_FALLBACK_MODEL,
|
||||
"tool_fallback_reasoning_effort": settings.OPENAI_TOOL_FALLBACK_REASONING_EFFORT,
|
||||
"tool_fallback_max_retries": settings.OPENAI_TOOL_FALLBACK_MAX_RETRIES,
|
||||
"tool_fallback_no_progress_attempts": settings.ARTEMIS_TOOL_FALLBACK_MAX_NO_PROGRESS_ATTEMPTS,
|
||||
"reasoning_effort": settings.OPENAI_REASONING_EFFORT,
|
||||
"model_presets": [
|
||||
"gpt-5.6-luna",
|
||||
@@ -86,6 +100,20 @@ def save_model_settings(update: ModelSettingsUpdate) -> dict:
|
||||
"OPENAI_RETRY_INTERVAL_SECONDS": str(update.retry_interval_seconds),
|
||||
"OPENAI_REASONING_EFFORT": update.reasoning_effort,
|
||||
}
|
||||
optional_settings = {
|
||||
"rate_limit_retry_seconds": ("OPENAI_RATE_LIMIT_RETRY_SECONDS", float),
|
||||
"tool_fallback_model": ("OPENAI_TOOL_FALLBACK_MODEL", str),
|
||||
"tool_fallback_reasoning_effort": ("OPENAI_TOOL_FALLBACK_REASONING_EFFORT", str),
|
||||
"tool_fallback_max_retries": ("OPENAI_TOOL_FALLBACK_MAX_RETRIES", int),
|
||||
"tool_fallback_no_progress_attempts": (
|
||||
"ARTEMIS_TOOL_FALLBACK_MAX_NO_PROGRESS_ATTEMPTS",
|
||||
int,
|
||||
),
|
||||
}
|
||||
for field_name, (env_name, expected_type) in optional_settings.items():
|
||||
value = getattr(update, field_name)
|
||||
if value is not None:
|
||||
values[env_name] = str(value)
|
||||
if update.api_key and update.api_key.get_secret_value().strip():
|
||||
values["OPENAI_API_KEY"] = update.api_key.get_secret_value().strip()
|
||||
persist_environment(values)
|
||||
@@ -95,6 +123,10 @@ def save_model_settings(update: ModelSettingsUpdate) -> dict:
|
||||
settings.OPENAI_MAX_RETRIES = update.max_retries
|
||||
settings.OPENAI_RETRY_INTERVAL_SECONDS = update.retry_interval_seconds
|
||||
settings.OPENAI_REASONING_EFFORT = update.reasoning_effort
|
||||
for field_name, (env_name, expected_type) in optional_settings.items():
|
||||
value = getattr(update, field_name)
|
||||
if value is not None:
|
||||
setattr(settings, env_name, expected_type(value))
|
||||
if "OPENAI_API_KEY" in values:
|
||||
settings.OPENAI_API_KEY = SecretStr(values["OPENAI_API_KEY"])
|
||||
from artemis.llm.router import ModelFactory
|
||||
|
||||
@@ -734,9 +734,12 @@ def export_batch(batch_id: str) -> Path:
|
||||
for path in outputs:
|
||||
archive.write(path, path.name)
|
||||
for number, report in enumerate(records, 1):
|
||||
prefix = f"任务报告/{number:04d}_{archive_case_name(report)}"
|
||||
for path in export_session(report["id"], formats):
|
||||
archive.write(
|
||||
path, f"任务报告/{number:04d}_{archive_case_name(report)}/{path.name}"
|
||||
)
|
||||
archive.write(path, f"{prefix}/{path.name}")
|
||||
for source, name in session_screenshots(report):
|
||||
archive.write(source, f"{prefix}/步骤截图/{name}")
|
||||
for source, name in session_videos(report):
|
||||
archive.write(source, f"{prefix}/任务录屏/{name}")
|
||||
temporary.replace(target)
|
||||
return target
|
||||
Reference in new issue
Block a user