Spaces:
Running
Running
Sync provider sampling omit runtime
Browse files- README.md +3 -2
- VERSION +1 -1
- agent_base/provider_compat.py +30 -3
- agent_base/react_agent.py +37 -4
README.md
CHANGED
|
@@ -162,8 +162,9 @@ Configure these as Hugging Face Space secrets before starting the app:
|
|
| 162 |
| `RH_SPACE_RETENTION_SECONDS` | `21600` | Delete inactive runs older than this many seconds. |
|
| 163 |
| `RH_SPACE_MAX_RUNS` | `40` | Keep at most this many inactive runs. |
|
| 164 |
| `RH_SPACE_CLEANUP_INTERVAL_SECONDS` | `900` | Background cleanup interval. |
|
| 165 |
-
| `WEBFETCH_TIMEOUT_SECONDS` | `
|
| 166 |
-
| `WEBFETCH_MAX_CHARS` | `
|
|
|
|
| 167 |
| `RH_COLLECTION_ENABLED` | `true` | Automatically collect completed hosted runs. |
|
| 168 |
| `RH_COLLECTION_DATASET_REPO` | `InternScience/ResearchHarness-Data` | Dataset repo that receives trajectory PRs. |
|
| 169 |
| `RH_COLLECTION_BATCH_SIZE` | `5` | Create one dataset PR after this many collected runs. |
|
|
|
|
| 162 |
| `RH_SPACE_RETENTION_SECONDS` | `21600` | Delete inactive runs older than this many seconds. |
|
| 163 |
| `RH_SPACE_MAX_RUNS` | `40` | Keep at most this many inactive runs. |
|
| 164 |
| `RH_SPACE_CLEANUP_INTERVAL_SECONDS` | `900` | Background cleanup interval. |
|
| 165 |
+
| `WEBFETCH_TIMEOUT_SECONDS` | `300` | Overall timeout for one WebFetch tool call. |
|
| 166 |
+
| `WEBFETCH_MAX_CHARS` | `16384` | Hard maximum characters returned by one URL-only WebFetch call. |
|
| 167 |
+
| `OMIT_GENERATE_PARAMS` | empty | Comma-separated sampling params not sent to providers that reject them, for example `presence_penalty`. |
|
| 168 |
| `RH_COLLECTION_ENABLED` | `true` | Automatically collect completed hosted runs. |
|
| 169 |
| `RH_COLLECTION_DATASET_REPO` | `InternScience/ResearchHarness-Data` | Dataset repo that receives trajectory PRs. |
|
| 170 |
| `RH_COLLECTION_BATCH_SIZE` | `5` | Create one dataset PR after this many collected runs. |
|
VERSION
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
v0.0.
|
|
|
|
| 1 |
+
v0.0.52
|
agent_base/provider_compat.py
CHANGED
|
@@ -1,8 +1,29 @@
|
|
| 1 |
import re
|
|
|
|
| 2 |
from typing import Any
|
| 3 |
|
| 4 |
|
| 5 |
_MODEL_NAME_SPLIT_RE = re.compile(r"[/:\s]+")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
|
| 8 |
def model_rejects_sampling_params(model_name: str) -> bool:
|
|
@@ -28,12 +49,18 @@ def apply_sampling_params(
|
|
| 28 |
temperature: Any = None,
|
| 29 |
top_p: Any = None,
|
| 30 |
presence_penalty: Any = None,
|
|
|
|
| 31 |
) -> None:
|
|
|
|
| 32 |
if model_rejects_sampling_params(model_name):
|
| 33 |
return
|
| 34 |
-
if temperature is not None:
|
| 35 |
request_kwargs["temperature"] = temperature
|
| 36 |
-
if top_p is not None:
|
| 37 |
request_kwargs["top_p"] = top_p
|
| 38 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
request_kwargs["presence_penalty"] = presence_penalty
|
|
|
|
| 1 |
import re
|
| 2 |
+
from collections.abc import Iterable
|
| 3 |
from typing import Any
|
| 4 |
|
| 5 |
|
| 6 |
_MODEL_NAME_SPLIT_RE = re.compile(r"[/:\s]+")
|
| 7 |
+
OMITTABLE_GENERATE_PARAMS = frozenset({"temperature", "top_p", "presence_penalty"})
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def normalize_omit_generate_params(value: Any) -> tuple[str, ...]:
|
| 11 |
+
if value is None or value == "":
|
| 12 |
+
return ()
|
| 13 |
+
if isinstance(value, str):
|
| 14 |
+
parts = [part.strip() for part in value.split(",")]
|
| 15 |
+
elif isinstance(value, dict):
|
| 16 |
+
raise ValueError("omit_generate_params must be a string or a sequence of strings.")
|
| 17 |
+
elif isinstance(value, Iterable):
|
| 18 |
+
parts = [str(part).strip() for part in value]
|
| 19 |
+
else:
|
| 20 |
+
raise ValueError("omit_generate_params must be a string or a sequence of strings.")
|
| 21 |
+
normalized = tuple(part for part in parts if part)
|
| 22 |
+
unknown = sorted(set(normalized) - OMITTABLE_GENERATE_PARAMS)
|
| 23 |
+
if unknown:
|
| 24 |
+
allowed = ", ".join(sorted(OMITTABLE_GENERATE_PARAMS))
|
| 25 |
+
raise ValueError(f"Unsupported omit_generate_params values: {unknown}. Allowed values: {allowed}.")
|
| 26 |
+
return normalized
|
| 27 |
|
| 28 |
|
| 29 |
def model_rejects_sampling_params(model_name: str) -> bool:
|
|
|
|
| 49 |
temperature: Any = None,
|
| 50 |
top_p: Any = None,
|
| 51 |
presence_penalty: Any = None,
|
| 52 |
+
omit_generate_params: Any = None,
|
| 53 |
) -> None:
|
| 54 |
+
omitted = set(normalize_omit_generate_params(omit_generate_params))
|
| 55 |
if model_rejects_sampling_params(model_name):
|
| 56 |
return
|
| 57 |
+
if temperature is not None and "temperature" not in omitted:
|
| 58 |
request_kwargs["temperature"] = temperature
|
| 59 |
+
if top_p is not None and "top_p" not in omitted:
|
| 60 |
request_kwargs["top_p"] = top_p
|
| 61 |
+
if (
|
| 62 |
+
presence_penalty is not None
|
| 63 |
+
and "presence_penalty" not in omitted
|
| 64 |
+
and not model_rejects_presence_penalty(model_name)
|
| 65 |
+
):
|
| 66 |
request_kwargs["presence_penalty"] = presence_penalty
|
agent_base/react_agent.py
CHANGED
|
@@ -16,7 +16,7 @@ from agent_base.base import BaseAgent
|
|
| 16 |
from agent_base.console_utils import ConsoleEventPrinter
|
| 17 |
from agent_base.context_compact import compact_messages, should_compact_messages
|
| 18 |
from agent_base.model_profiles import resolve_model_profile
|
| 19 |
-
from agent_base.provider_compat import apply_sampling_params
|
| 20 |
from agent_base.prompt import composed_system_prompt
|
| 21 |
from agent_base.session_state import AgentSessionState, CompactionRecord, persist_session_state, resolve_session_state_path
|
| 22 |
from agent_base.trace_utils import FlatTraceWriter
|
|
@@ -632,10 +632,17 @@ def image_context_trace_text(result: Any) -> str:
|
|
| 632 |
return text
|
| 633 |
|
| 634 |
|
| 635 |
-
def default_llm_config(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 636 |
selected_model = str(model_name or "").strip() or default_model_name()
|
| 637 |
if extra_body is not None and not isinstance(extra_body, dict):
|
| 638 |
raise ValueError("extra_body must be a dict.")
|
|
|
|
|
|
|
|
|
|
| 639 |
config = {
|
| 640 |
"model": selected_model,
|
| 641 |
"api_key": os.environ.get("API_KEY", "EMPTY"),
|
|
@@ -656,6 +663,8 @@ def default_llm_config(model_name: Optional[str] = None, extra_body: Optional[di
|
|
| 656 |
"presence_penalty": float(os.environ.get("PRESENCE_PENALTY", str(DEFAULT_PRESENCE_PENALTY))),
|
| 657 |
},
|
| 658 |
}
|
|
|
|
|
|
|
| 659 |
if extra_body:
|
| 660 |
config["extra_body"] = dict(extra_body)
|
| 661 |
return config
|
|
@@ -751,7 +760,14 @@ class MultiTurnReactAgent(BaseAgent):
|
|
| 751 |
self.tool_map = {tool_name: tool_registry[tool_name] for tool_name in requested_tools}
|
| 752 |
self.tool_names = list(self.tool_map.keys())
|
| 753 |
self.model = str(llm["model"])
|
| 754 |
-
self.llm_generate_cfg = llm["generate_cfg"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 755 |
self.trace_dir = Path(trace_dir) if trace_dir else None
|
| 756 |
self.trace_path: Optional[Path] = None
|
| 757 |
self.session_state_path: Optional[Path] = None
|
|
@@ -837,6 +853,7 @@ class MultiTurnReactAgent(BaseAgent):
|
|
| 837 |
if presence_penalty is not None
|
| 838 |
else self.llm_generate_cfg.get("presence_penalty", 1.1)
|
| 839 |
),
|
|
|
|
| 840 |
)
|
| 841 |
if self.llm_extra_body:
|
| 842 |
request_kwargs["extra_body"] = dict(self.llm_extra_body)
|
|
@@ -1578,6 +1595,17 @@ def _parse_cli_args(
|
|
| 1578 |
metavar="JSON",
|
| 1579 |
help="Provider-specific OpenAI-compatible request extra_body JSON object.",
|
| 1580 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1581 |
args = parser.parse_args(argv)
|
| 1582 |
if args.tool_names and args.extra_tools:
|
| 1583 |
raise ValueError("--tool defines the complete tool set and cannot be combined with --extra-tool.")
|
|
@@ -1590,6 +1618,9 @@ def _parse_cli_args(
|
|
| 1590 |
if not isinstance(parsed_extra_body, dict):
|
| 1591 |
raise ValueError("--llm-extra-body-json must be a JSON object.")
|
| 1592 |
llm_extra_body = parsed_extra_body
|
|
|
|
|
|
|
|
|
|
| 1593 |
|
| 1594 |
prompt_text = ""
|
| 1595 |
if args.prompt_file:
|
|
@@ -1611,6 +1642,7 @@ def _parse_cli_args(
|
|
| 1611 |
validate_named_tools(args.tool_names) if args.tool_names else [],
|
| 1612 |
resolve_extra_tool_names(args.extra_tools),
|
| 1613 |
llm_extra_body,
|
|
|
|
| 1614 |
)
|
| 1615 |
|
| 1616 |
|
|
@@ -1629,6 +1661,7 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|
| 1629 |
tool_names,
|
| 1630 |
extra_tools,
|
| 1631 |
llm_extra_body,
|
|
|
|
| 1632 |
) = _parse_cli_args(argv or sys.argv[1:])
|
| 1633 |
agent_cls = resolve_agent_class_for_role_prompt_files(role_prompt_files)
|
| 1634 |
forbidden_tools = set(getattr(agent_cls, "forbidden_tool_names", set()))
|
|
@@ -1644,7 +1677,7 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|
| 1644 |
if extra_tools
|
| 1645 |
else None
|
| 1646 |
),
|
| 1647 |
-
llm=default_llm_config(extra_body=llm_extra_body),
|
| 1648 |
trace_dir=trace_dir,
|
| 1649 |
role_prompt=role_prompt or None,
|
| 1650 |
)
|
|
|
|
| 16 |
from agent_base.console_utils import ConsoleEventPrinter
|
| 17 |
from agent_base.context_compact import compact_messages, should_compact_messages
|
| 18 |
from agent_base.model_profiles import resolve_model_profile
|
| 19 |
+
from agent_base.provider_compat import apply_sampling_params, normalize_omit_generate_params
|
| 20 |
from agent_base.prompt import composed_system_prompt
|
| 21 |
from agent_base.session_state import AgentSessionState, CompactionRecord, persist_session_state, resolve_session_state_path
|
| 22 |
from agent_base.trace_utils import FlatTraceWriter
|
|
|
|
| 632 |
return text
|
| 633 |
|
| 634 |
|
| 635 |
+
def default_llm_config(
|
| 636 |
+
model_name: Optional[str] = None,
|
| 637 |
+
extra_body: Optional[dict[str, Any]] = None,
|
| 638 |
+
omit_generate_params: Optional[str | Sequence[str]] = None,
|
| 639 |
+
) -> dict:
|
| 640 |
selected_model = str(model_name or "").strip() or default_model_name()
|
| 641 |
if extra_body is not None and not isinstance(extra_body, dict):
|
| 642 |
raise ValueError("extra_body must be a dict.")
|
| 643 |
+
resolved_omit_generate_params = normalize_omit_generate_params(
|
| 644 |
+
omit_generate_params if omit_generate_params is not None else os.environ.get("OMIT_GENERATE_PARAMS")
|
| 645 |
+
)
|
| 646 |
config = {
|
| 647 |
"model": selected_model,
|
| 648 |
"api_key": os.environ.get("API_KEY", "EMPTY"),
|
|
|
|
| 663 |
"presence_penalty": float(os.environ.get("PRESENCE_PENALTY", str(DEFAULT_PRESENCE_PENALTY))),
|
| 664 |
},
|
| 665 |
}
|
| 666 |
+
if resolved_omit_generate_params:
|
| 667 |
+
config["generate_cfg"]["omit_generate_params"] = resolved_omit_generate_params
|
| 668 |
if extra_body:
|
| 669 |
config["extra_body"] = dict(extra_body)
|
| 670 |
return config
|
|
|
|
| 760 |
self.tool_map = {tool_name: tool_registry[tool_name] for tool_name in requested_tools}
|
| 761 |
self.tool_names = list(self.tool_map.keys())
|
| 762 |
self.model = str(llm["model"])
|
| 763 |
+
self.llm_generate_cfg = dict(llm["generate_cfg"])
|
| 764 |
+
resolved_omit_generate_params = normalize_omit_generate_params(
|
| 765 |
+
self.llm_generate_cfg.get("omit_generate_params")
|
| 766 |
+
)
|
| 767 |
+
if resolved_omit_generate_params:
|
| 768 |
+
self.llm_generate_cfg["omit_generate_params"] = resolved_omit_generate_params
|
| 769 |
+
else:
|
| 770 |
+
self.llm_generate_cfg.pop("omit_generate_params", None)
|
| 771 |
self.trace_dir = Path(trace_dir) if trace_dir else None
|
| 772 |
self.trace_path: Optional[Path] = None
|
| 773 |
self.session_state_path: Optional[Path] = None
|
|
|
|
| 853 |
if presence_penalty is not None
|
| 854 |
else self.llm_generate_cfg.get("presence_penalty", 1.1)
|
| 855 |
),
|
| 856 |
+
omit_generate_params=self.llm_generate_cfg.get("omit_generate_params"),
|
| 857 |
)
|
| 858 |
if self.llm_extra_body:
|
| 859 |
request_kwargs["extra_body"] = dict(self.llm_extra_body)
|
|
|
|
| 1595 |
metavar="JSON",
|
| 1596 |
help="Provider-specific OpenAI-compatible request extra_body JSON object.",
|
| 1597 |
)
|
| 1598 |
+
parser.add_argument(
|
| 1599 |
+
"--omit-generate-param",
|
| 1600 |
+
action="append",
|
| 1601 |
+
default=[],
|
| 1602 |
+
dest="omit_generate_params",
|
| 1603 |
+
metavar="NAME",
|
| 1604 |
+
help=(
|
| 1605 |
+
"Do not send one default sampling parameter to the provider. "
|
| 1606 |
+
"Allowed: temperature, top_p, presence_penalty. May be passed multiple times."
|
| 1607 |
+
),
|
| 1608 |
+
)
|
| 1609 |
args = parser.parse_args(argv)
|
| 1610 |
if args.tool_names and args.extra_tools:
|
| 1611 |
raise ValueError("--tool defines the complete tool set and cannot be combined with --extra-tool.")
|
|
|
|
| 1618 |
if not isinstance(parsed_extra_body, dict):
|
| 1619 |
raise ValueError("--llm-extra-body-json must be a JSON object.")
|
| 1620 |
llm_extra_body = parsed_extra_body
|
| 1621 |
+
omit_generate_params = (
|
| 1622 |
+
normalize_omit_generate_params(args.omit_generate_params) if args.omit_generate_params else None
|
| 1623 |
+
)
|
| 1624 |
|
| 1625 |
prompt_text = ""
|
| 1626 |
if args.prompt_file:
|
|
|
|
| 1642 |
validate_named_tools(args.tool_names) if args.tool_names else [],
|
| 1643 |
resolve_extra_tool_names(args.extra_tools),
|
| 1644 |
llm_extra_body,
|
| 1645 |
+
omit_generate_params,
|
| 1646 |
)
|
| 1647 |
|
| 1648 |
|
|
|
|
| 1661 |
tool_names,
|
| 1662 |
extra_tools,
|
| 1663 |
llm_extra_body,
|
| 1664 |
+
omit_generate_params,
|
| 1665 |
) = _parse_cli_args(argv or sys.argv[1:])
|
| 1666 |
agent_cls = resolve_agent_class_for_role_prompt_files(role_prompt_files)
|
| 1667 |
forbidden_tools = set(getattr(agent_cls, "forbidden_tool_names", set()))
|
|
|
|
| 1677 |
if extra_tools
|
| 1678 |
else None
|
| 1679 |
),
|
| 1680 |
+
llm=default_llm_config(extra_body=llm_extra_body, omit_generate_params=omit_generate_params),
|
| 1681 |
trace_dir=trace_dir,
|
| 1682 |
role_prompt=role_prompt or None,
|
| 1683 |
)
|