diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a87a1e..a1723b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,8 +12,8 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.11", "3.12", "3.13"] - os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ["3.12"] + os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -36,7 +36,6 @@ jobs: run: python -m pytest --cov=vantage --cov-report=xml -q - name: Upload coverage - if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' uses: codecov/codecov-action@v4 with: files: coverage.xml diff --git a/pyproject.toml b/pyproject.toml index f017f2d..561f2d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ dev = [ "respx>=0.21.0", "ruff>=0.6.0", "mypy>=1.10.0", + "types-PyYAML>=0.1.0", # For mypy type checking support ] [project.urls] diff --git a/src/vantage/llms/groq.py b/src/vantage/llms/groq.py index dc6d403..1b3e722 100644 --- a/src/vantage/llms/groq.py +++ b/src/vantage/llms/groq.py @@ -8,7 +8,7 @@ from ..core.bases import AsyncLLMBase, LLMBase, ToolBase from ..core.models import Message -from .openai import _auth_headers, _parse_response, _to_openai_message, _to_openai_tool +from .openai import _auth_headers, _parse_response, _to_openai_message class GroqModel(LLMBase): @@ -183,11 +183,24 @@ def _groq_payload( if top_p is not None: payload["top_p"] = top_p - tool_payload = [_to_openai_tool(t) for t in tools] + tool_payload = [_to_groq_tool(t) for t in tools] if tool_payload: payload["tools"] = tool_payload return msgs, payload +def _to_groq_tool(t: ToolBase) -> Dict[str, Any]: + """Like _to_openai_tool but strips `additionalProperties` which Groq rejects.""" + schema = {k: v for k, v in t.input_schema().items() if k != "additionalProperties"} + return { + "type": "function", + "function": { + "name": t.name, + "description": t.description, + "parameters": schema, + }, + } + + diff --git a/src/vantage/llms/openai.py b/src/vantage/llms/openai.py index 26659bd..5baff48 100644 --- a/src/vantage/llms/openai.py +++ b/src/vantage/llms/openai.py @@ -200,7 +200,8 @@ def _parse_response(data: Dict[str, Any]) -> Message: fn = tc.get("function") or {} args_raw = fn.get("arguments") or "{}" try: - args = json.loads(args_raw) if isinstance(args_raw, str) else dict(args_raw) + parsed = json.loads(args_raw) if isinstance(args_raw, str) else args_raw + args = parsed if isinstance(parsed, dict) else {} except Exception: args = {} tool_calls.append( diff --git a/src/vantage/tools/calculator.py b/src/vantage/tools/calculator.py index 25b2e7e..10df617 100644 --- a/src/vantage/tools/calculator.py +++ b/src/vantage/tools/calculator.py @@ -5,6 +5,8 @@ from typing import Any, Dict from ..core.bases import ToolBase +from typing import Callable + # Guard against exponentiation towers like 9**9**9 that would compute for a very long time. _MAX_POW_BASE = 1_000 @@ -42,7 +44,7 @@ def execute(self, **kwargs: Any) -> str: return str(value) -_BIN_OPS = { +_BIN_OPS: dict[type, Callable[[float, float], float]] = { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, @@ -52,7 +54,7 @@ def execute(self, **kwargs: Any) -> str: ast.Pow: operator.pow, } -_UNARY_OPS = { +_UNARY_OPS: dict[type, Callable[[float], float]] = { ast.UAdd: operator.pos, ast.USub: operator.neg, } diff --git a/src/vantage/utils/viz.py b/src/vantage/utils/viz.py index 4912575..912e7a7 100644 --- a/src/vantage/utils/viz.py +++ b/src/vantage/utils/viz.py @@ -38,7 +38,7 @@ _DEFAULT_STYLE: dict = {"outline": (80, 90, 100), "label_c": (160, 170, 180), "badge": "STEP"} -def _load_fonts() -> tuple[ImageFont.ImageFont, ImageFont.ImageFont]: +def _load_fonts() -> tuple: """Return ``(content_font, label_font)``. Falls back to Pillow default.""" for name in ("DejaVuSans.ttf", "arial.ttf", "Arial.ttf", "FreeSans.ttf"): try: diff --git a/tests/test_config.py b/tests/test_config.py index 3b65749..b32e268 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -23,8 +23,9 @@ def _write(tmp_path, content: str): @respx.mock -def test_flat_format_groq(tmp_path): +def test_flat_format_groq(tmp_path, monkeypatch): """New flat 'model: groq/...' format should load and call Groq endpoint.""" + monkeypatch.setenv("GROQ_API_KEY", "test") cfg = _write(tmp_path, """ agents: bot: diff --git a/tests/test_yaml_and_llms.py b/tests/test_yaml_and_llms.py index 9f2bc21..3142508 100644 --- a/tests/test_yaml_and_llms.py +++ b/tests/test_yaml_and_llms.py @@ -1,3 +1,4 @@ + import respx import httpx