Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ jobs:
python -m pip install poetry
python -m poetry install --extras "dev ml"

- name: Validate isolated MCP fixture environment
if: matrix.python-version == '3.12'
run: |
cd tools/mcp-fixtures
python -m poetry install
python -m poetry run python -c "import fastmcp; print(fastmcp.__version__)"

- name: Run ruff linting
run: python -m poetry run ruff check tactus/ tests/ features/steps/

Expand Down
12 changes: 11 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.PHONY: help generate-parsers generate-python-parser generate-typescript-parser test-parsers clean-generated dev-ide test-examples test-examples-fast test-examples-parallel test-examples-bdd
.PHONY: test-docker-sandbox
.PHONY: test-docker-sandbox mcp-fixtures-install mcp-fixtures-smoke

help:
@echo "Tactus Parser Generation and Testing"
Expand All @@ -21,6 +21,10 @@ help:
@echo "Docker Sandbox Testing:"
@echo " test-docker-sandbox - Run opt-in Docker sandbox smoke tests"
@echo ""
@echo "MCP Fixtures:"
@echo " mcp-fixtures-install - Install isolated MCP fixture dependencies"
@echo " mcp-fixtures-smoke - Verify isolated MCP fixture environment"
@echo ""
@echo "Requirements:"
@echo " - Docker must be running (for parser generation)"
@echo " - Python 3.10+ with dependencies installed"
Expand Down Expand Up @@ -130,4 +134,10 @@ test-docker-sandbox:
@echo "Pre-req: tactus sandbox rebuild --force"
TACTUS_RUN_DOCKER_TESTS=1 python3 -m poetry run pytest -m docker -v --tb=short

# Isolated MCP fixture dependency environment (separate from root lock)
mcp-fixtures-install:
cd tools/mcp-fixtures && python3 -m poetry install

mcp-fixtures-smoke: mcp-fixtures-install
cd tools/mcp-fixtures && python3 -m poetry run python -c "import fastmcp; print(fastmcp.__version__)"

24 changes: 15 additions & 9 deletions features/steps/dspy_lm_steps.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
"""Step definitions for DSPy Language Model configuration."""

import os
import uuid
from behave import given, when, then


def _test_api_credential() -> str:
"""Return a non-secret credential value for test-only LM configuration."""
return os.environ.get("TACTUS_TEST_API_CREDENTIAL") or f"test-{uuid.uuid4()}"


@given("dspy is installed as a dependency")
def step_dspy_installed(context):
"""Verify DSPy is installed."""
Expand All @@ -20,15 +26,15 @@ def step_lm_configured_with_model(context, model):
"""Configure an LM with specified model."""
from tactus.dspy import configure_lm

context.lm = configure_lm(model, api_key="test-key")
context.lm = configure_lm(model, api_key=_test_api_credential())


@when('I configure an LM with model "{model}" and temperature {temperature:f}')
def step_configure_lm_with_temperature(context, model, temperature):
"""Configure an LM with custom temperature."""
from tactus.dspy import configure_lm

context.lm = configure_lm(model, temperature=temperature, api_key="test-key")
context.lm = configure_lm(model, temperature=temperature, api_key=_test_api_credential())
context.lm_temperature = temperature


Expand All @@ -37,7 +43,7 @@ def step_configure_lm_with_max_tokens(context, model, max_tokens):
"""Configure an LM with max_tokens parameter."""
from tactus.dspy import configure_lm

context.lm = configure_lm(model, max_tokens=max_tokens, api_key="test-key")
context.lm = configure_lm(model, max_tokens=max_tokens, api_key=_test_api_credential())
context.lm_max_tokens = max_tokens


Expand Down Expand Up @@ -82,7 +88,7 @@ def step_configure_another_lm(context, model):
"""Configure another LM, replacing the current one."""
from tactus.dspy import configure_lm

context.lm = configure_lm(model, api_key="test-key")
context.lm = configure_lm(model, api_key=_test_api_credential())
context.current_model = model


Expand Down Expand Up @@ -231,7 +237,7 @@ def step_configure_lm_with_api_base(context, model, api_base):
"""Configure LM with custom API base URL."""
from tactus.dspy import configure_lm

context.lm = configure_lm(model, api_base=api_base, api_key="test-key")
context.lm = configure_lm(model, api_base=api_base, api_key=_test_api_credential())
context.lm_api_base = api_base


Expand Down Expand Up @@ -278,7 +284,7 @@ def step_configure_lm_with_region(context, model, region):
"""Configure LM with AWS region for Bedrock."""
from tactus.dspy import configure_lm

context.lm = configure_lm(model, region=region, api_key="test-key")
context.lm = configure_lm(model, region=region, api_key=_test_api_credential())
context.lm_region = region


Expand Down Expand Up @@ -317,7 +323,7 @@ def step_try_configure_invalid_lm(context, model):
from tactus.dspy import configure_lm

try:
context.lm = configure_lm(model, api_key="test-key")
context.lm = configure_lm(model, api_key=_test_api_credential())
# Force error for clearly invalid models
if "invalid" in model.lower() or "/" not in model:
raise ValueError(f"Invalid model: {model}")
Expand All @@ -334,7 +340,7 @@ def step_try_configure_no_model(context):

try:
# Try to call with no model - should fail
context.lm = configure_lm(None, api_key="test-key")
context.lm = configure_lm(None, api_key=_test_api_credential())
context.error = None
except Exception as e:
context.error = e
Expand All @@ -348,4 +354,4 @@ def step_configure_lm_simple(context, model):
"""Configure an LM with the given model (basic form - catches anything not matched above)."""
from tactus.dspy import configure_lm

context.lm = configure_lm(model, api_key="test-key")
context.lm = configure_lm(model, api_key=_test_api_credential())
Loading
Loading