470cf75722
Merged upstream/main (418abca, release v0.3.0) into our fork on a
dedicated branch. 21 conflicting files resolved; main worktree untouched.
Resolution policy and key decisions:
- Keep Ai4Sci runtime endpoints, durable dispatch, workspace scopes and
the HITL/DynamicReview approval chain (approval path is product-critical).
- Adopt upstream model registry (llm/registry.py): our 136 model entries
are a strict subset of upstream's 180, so dropping our inline table
loses nothing and gains 44 new models.
- Adopt upstream native EvoChatDeepSeek; drop our obsolete
_patch_deepseek_reasoning_passback monkey patch.
- Keep our six patches.py additions, ported onto upstream's new
_OpenAICompatContent class: stable tool-call ids, tool-history
sanitization, drop_reasoning_metadata, empty-SSE keepalive,
extracted-document-text patch, _has_assistant_tool_protocol.
- Keep our skill-budget middleware path (skills=None) instead of passing
skills through, to avoid double loading.
- Keep sanitized error labels (_safe_error_label) while adopting
upstream's injected MiddlewareEventSink for fallback narration.
- Keep port 3076 and the LANGGRAPH_SERVER_URL override; adopt upstream's
host/probe-host handling and CONFIG_DRIFT_SINCE_LAUNCH.
- Adopt upstream dependency stack: deepagents 0.7.6, langchain-quickjs
0.3.7, langgraph-api 0.14; keep our extra deps (rfc8785, pillow,
firecrawl-anydoc, nest-asyncio).
- Align call sites with upstream APIs: create_tool_selector_middleware
now takes events= instead of track_stream_selection=.
98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
"""Shared LangGraph SDK plumbing for the local langgraph-dev server."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from collections.abc import Mapping
|
|
|
|
DEFAULT_LANGGRAPH_DEV_PORT = 3076
|
|
# Mirrors ``config.langgraph_dev_host`` / ``manager._DEFAULT_HOST``. The value
|
|
# only matters as a stand-in for the *bind* host — ``_format_hostport`` runs it
|
|
# through ``_probe_host``, so both this and "0.0.0.0" yield the same client URL.
|
|
DEFAULT_LANGGRAPH_DEV_HOST = "127.0.0.1"
|
|
LANGGRAPH_DEV_AUTH_HEADERS = {"x-auth-scheme": "langsmith"}
|
|
|
|
|
|
def langgraph_dev_url(
|
|
config: object | None = None,
|
|
*,
|
|
port: int | None = None,
|
|
host: str | None = None,
|
|
) -> str:
|
|
"""Return the local langgraph-dev base URL for a config or explicit port/host.
|
|
|
|
An explicit ``LANGGRAPH_SERVER_URL`` (container / prod deploy) wins when no
|
|
port or host override is supplied. Otherwise the configured bind interface is
|
|
mapped through ``manager._probe_host``: a wildcard bind (``0.0.0.0``) still
|
|
resolves to loopback here, while a specific interface is honored so
|
|
self-dispatch keeps working when the server is pinned to one address.
|
|
"""
|
|
runtime_url = os.environ.get("LANGGRAPH_SERVER_URL", "").strip().rstrip("/")
|
|
if port is None and host is None and runtime_url:
|
|
return runtime_url
|
|
|
|
from .manager import _format_hostport
|
|
|
|
selected_port = (
|
|
int(port)
|
|
if port is not None
|
|
else int(getattr(config, "langgraph_dev_port", DEFAULT_LANGGRAPH_DEV_PORT))
|
|
)
|
|
selected_host = (
|
|
host
|
|
if host is not None
|
|
else str(
|
|
getattr(config, "langgraph_dev_host", DEFAULT_LANGGRAPH_DEV_HOST)
|
|
or DEFAULT_LANGGRAPH_DEV_HOST
|
|
)
|
|
)
|
|
return f"http://{_format_hostport(selected_host, selected_port)}"
|
|
|
|
|
|
def configured_langgraph_dev_url() -> str:
|
|
"""Return the local langgraph-dev URL from the effective application config."""
|
|
from ..EvoScientist import _ensure_config
|
|
|
|
return langgraph_dev_url(_ensure_config())
|
|
|
|
|
|
def langgraph_dev_headers(headers: Mapping[str, str] | None = None) -> dict[str, str]:
|
|
"""Return SDK headers, defaulting to the local langgraph-dev auth scheme."""
|
|
return dict(LANGGRAPH_DEV_AUTH_HEADERS if headers is None else headers)
|
|
|
|
|
|
def get_langgraph_sync_client(*, url: str, headers: Mapping[str, str] | None = None):
|
|
"""Build a sync LangGraph SDK client with EvoScientist's default headers."""
|
|
from langgraph_sdk import get_sync_client
|
|
|
|
return get_sync_client(url=url, headers=langgraph_dev_headers(headers))
|
|
|
|
|
|
def get_langgraph_async_client(*, url: str, headers: Mapping[str, str] | None = None):
|
|
"""Build an async LangGraph SDK client with EvoScientist's default headers."""
|
|
from langgraph_sdk import get_client
|
|
|
|
return get_client(url=url, headers=langgraph_dev_headers(headers))
|
|
|
|
|
|
def default_scheduler_timezone(config: object | None = None) -> str | None:
|
|
"""Return configured scheduler timezone, falling back to the host timezone."""
|
|
if config is None:
|
|
from ..EvoScientist import _ensure_config
|
|
|
|
config = _ensure_config()
|
|
timezone = str(getattr(config, "scheduler_default_timezone", "") or "")
|
|
if timezone:
|
|
return timezone
|
|
try:
|
|
from tzlocal import get_localzone_name
|
|
|
|
return get_localzone_name()
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def messages_input(content: str) -> dict[str, list[dict[str, str]]]:
|
|
"""Return the standard LangGraph chat input shape for one user message."""
|
|
return {"messages": [{"role": "user", "content": content}]}
|