Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""Meta Model API (Muse Spark) provider plugin for Hermes Agent.
|
||||
|
||||
Provider profile for Meta Superintelligence Labs' Muse Spark family, served
|
||||
via the OpenAI-compatible Meta Model API at ``https://api.meta.ai/v1``.
|
||||
|
||||
Bundled from https://github.com/albertodepaola/hermes-meta-provider. Hermes'
|
||||
provider discovery (``providers/__init__.py``) imports it on first
|
||||
``get_provider_profile()`` / ``list_providers()`` call, and the module-level
|
||||
``register_provider()`` below wires it into the registry.
|
||||
|
||||
Design notes
|
||||
------------
|
||||
* **Zero core edits.** Everything rides on ``ProviderProfile`` hooks. No changes
|
||||
to hermes' ``model_metadata.py`` / ``models.py`` / ``run_agent.py`` are needed:
|
||||
- Context window (1M), reasoning and vision capabilities already resolve from
|
||||
models.dev for the muse-spark family, so no static ctx table entry is required.
|
||||
- The reasoning dial is emitted as a **top-level ``reasoning_effort``** kwarg
|
||||
(returned in the ``top_level`` slot of ``build_api_kwargs_extras``), which the
|
||||
chat-completions transport merges unconditionally. This deliberately avoids
|
||||
the ``extra_body.reasoning`` path, whose emission is gated by a hardcoded
|
||||
host allowlist in core (``AIAgent._supports_reasoning_extra_body``) that a
|
||||
third-party plugin must not edit.
|
||||
* **Meta 400 on ``reasoning_effort: "none"``.** Muse rejects ``none``; disabling
|
||||
reasoning maps to ``"minimal"`` instead.
|
||||
* **``default_max_tokens=16384``.** Muse spends completion budget on hidden
|
||||
reasoning tokens first; small caps can finish with empty content.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from providers import register_provider
|
||||
from providers.base import ProviderProfile
|
||||
|
||||
|
||||
def _resolve_effort(reasoning_config: dict | None) -> str:
|
||||
"""Map Hermes' reasoning_config to a Meta-safe ``reasoning_effort`` value.
|
||||
|
||||
Meta's vocabulary (minimal..xhigh; rejects ``none``) is declared in
|
||||
agent.reasoning_effort. Disabled/"none" maps to ``minimal`` (the closest
|
||||
Meta has to off); unset/bespoke levels fall to ``medium``.
|
||||
"""
|
||||
rc = reasoning_config or {}
|
||||
if rc.get("enabled") is False:
|
||||
return "minimal"
|
||||
effort = str(rc.get("effort") or "").strip().lower()
|
||||
if effort in {"", "none"}:
|
||||
return "minimal" if effort == "none" else "medium"
|
||||
|
||||
from agent.reasoning_effort import META_AI_EFFORTS, clamp_effort
|
||||
|
||||
clamped = clamp_effort(effort, META_AI_EFFORTS)
|
||||
return clamped if clamped in META_AI_EFFORTS else "medium"
|
||||
|
||||
|
||||
class MetaAIProfile(ProviderProfile):
|
||||
"""Meta Model API — top-level reasoning_effort, self-contained."""
|
||||
|
||||
# Non-chat model prefixes excluded from the agent picker. The live
|
||||
# /v1/models catalog includes image-generation and transcription models
|
||||
# that are not suitable for agentic chat.
|
||||
_NON_CHAT_PREFIXES = ("muse-image-", "muse-voice-")
|
||||
|
||||
def fetch_models(
|
||||
self,
|
||||
*,
|
||||
api_key: str | None = None,
|
||||
base_url: str | None = None,
|
||||
timeout: float = 8.0,
|
||||
) -> list[str] | None:
|
||||
"""Fetch and filter the live catalog, excluding non-chat models."""
|
||||
live = super().fetch_models(api_key=api_key, base_url=base_url, timeout=timeout)
|
||||
if live is None:
|
||||
return None
|
||||
return [
|
||||
m for m in live
|
||||
if not any(m.startswith(p) for p in self._NON_CHAT_PREFIXES)
|
||||
]
|
||||
|
||||
def build_api_kwargs_extras(
|
||||
self,
|
||||
*,
|
||||
reasoning_config: dict | None = None,
|
||||
supports_reasoning: bool = False, # noqa: ARG002 — we self-gate below
|
||||
**context: Any,
|
||||
) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||
"""Emit ``reasoning_effort`` as a top-level api kwarg.
|
||||
|
||||
We ignore the core ``supports_reasoning`` gate on purpose: that flag is
|
||||
driven by a host allowlist in core we cannot (and should not) edit from
|
||||
an out-of-tree plugin. Muse Spark always accepts ``reasoning_effort``,
|
||||
so we resolve it from ``reasoning_config`` directly.
|
||||
"""
|
||||
return {}, {"reasoning_effort": _resolve_effort(reasoning_config)}
|
||||
|
||||
|
||||
def _base_url() -> str:
|
||||
"""Allow a base-URL override via ``META_BASE_URL`` without editing config."""
|
||||
return os.getenv("META_BASE_URL", "").strip() or "https://api.meta.ai/v1"
|
||||
|
||||
|
||||
meta_ai = MetaAIProfile(
|
||||
name="meta-ai",
|
||||
aliases=("meta", "muse", "muse-spark", "model-api", "msl"),
|
||||
display_name="Meta Model API",
|
||||
description="Meta Muse Spark family (Meta Superintelligence Labs)",
|
||||
signup_url="https://developer.meta.com/ai/",
|
||||
# MODEL_API_KEY is Meta's documented env var; the aliases are conveniences.
|
||||
env_vars=("MODEL_API_KEY", "META_API_KEY", "META_MODEL_API_KEY", "META_BASE_URL"),
|
||||
base_url=_base_url(),
|
||||
auth_type="api_key",
|
||||
# Responses API is the wire that engages Muse prompt caching: measured
|
||||
# 0 cached tokens on /v1/chat/completions vs 93-99% cache hits on
|
||||
# /v1/responses with prompt_cache_retention (see host_mandated_api_mode
|
||||
# in hermes_cli/providers.py and the retention hint in
|
||||
# agent/transports/codex.py). The MetaAIProfile chat-completions hook
|
||||
# above still covers custom OpenAI-compatible endpoints configured with
|
||||
# a non-api.meta.ai base URL, which fall through to chat_completions.
|
||||
api_mode="codex_responses",
|
||||
# Muse Spark is natively multimodal (image/video/pdf/audio in, text out).
|
||||
supports_vision=True,
|
||||
# ...but only on user turns: an image envelope inside a role:tool message
|
||||
# 400s "messages[N].content did not match any supported type" (#101668).
|
||||
supports_vision_tool_messages=False,
|
||||
# Cheap contributor tier is a good default for auxiliary tasks
|
||||
# (compaction, title generation, vision) when this is the main provider.
|
||||
default_aux_model="muse-spark-1.2-contributor",
|
||||
# Muse spends completion budget on hidden reasoning tokens first; a low cap
|
||||
# can finish with empty content. 16k is a safe floor.
|
||||
default_max_tokens=16384,
|
||||
# Minimal fallback shown when the live /v1/models fetch fails or no
|
||||
# credentials are configured yet. Keep this list small — just enough so
|
||||
# the picker isn't empty when the API is unreachable.
|
||||
fallback_models=(
|
||||
"muse-spark-1.2",
|
||||
),
|
||||
)
|
||||
|
||||
register_provider(meta_ai)
|
||||
@@ -0,0 +1,6 @@
|
||||
name: meta-ai-provider
|
||||
kind: model-provider
|
||||
version: 1.0.0
|
||||
description: Meta Model API — Muse Spark family (Meta Superintelligence Labs)
|
||||
author: Beto de Paola
|
||||
homepage: https://github.com/albertodepaola/hermes-meta-provider
|
||||
Reference in New Issue
Block a user