| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- """Tests for lambdagent.fromconfig compiler."""
- import os
- import json
- import tempfile
- import pytest
- import yaml
- from lambdagent.fromconfig import from_config, lint_config, to_lambda_expr, describe_config
- from lambdagent.fromconfig.errors import SchemaError
- from lambdagent.fromconfig.schema import validate_schema
- from lambdagent.primitives import Lam, Loop, Compose
- from lambdagent.extensions import Memory, Guard, Route
- from lambdagent.conversation import ConversationLam
- def _write_config(config):
- f = tempfile.NamedTemporaryFile(mode='w', suffix='.yml', delete=False, encoding='utf-8')
- yaml.dump(config, f, allow_unicode=True)
- f.close()
- return f.name
- def test_compile_simple():
- path = _write_config({"type": "simple", "systemPrompt": "Hello", "model": {"name": "test"}})
- try:
- term = from_config(path)
- # The compiler may emit either Lam (stateless) or ConversationLam
- # (history-aware) for a "simple" agent depending on provider config.
- # Both are valid Lambda abstractions — accept either.
- assert isinstance(term, (Lam, ConversationLam)), \
- f"expected Lam or ConversationLam, got {type(term).__name__}"
- finally:
- os.unlink(path)
- def test_compile_chain():
- path = _write_config({
- "type": "chain", "model": {"name": "test"},
- "chain": {"steps": [
- {"name": "s1", "prompt": "Step 1"},
- {"name": "s2", "prompt": "Step 2"},
- ]}
- })
- try:
- term = from_config(path)
- assert isinstance(term, Compose)
- assert len(term.stages) == 2
- finally:
- os.unlink(path)
- def test_compile_react():
- path = _write_config({
- "type": "react", "systemPrompt": "Think",
- "model": {"name": "test"},
- "react": {"maxSteps": 5},
- "mcp": {"localTools": ["terminate"]},
- })
- try:
- term = from_config(path)
- assert isinstance(term, Loop)
- assert term.max_steps == 5
- finally:
- os.unlink(path)
- def test_compile_with_memory():
- path = _write_config({
- "type": "simple", "systemPrompt": "Hello",
- "model": {"name": "test"},
- "memory": {"enabled": True, "strategy": "local", "size": 10, "ttl": 3600},
- })
- try:
- term = from_config(path)
- assert isinstance(term, Memory)
- finally:
- os.unlink(path)
- def test_compile_with_guard():
- path = _write_config({
- "type": "simple", "systemPrompt": "Hello",
- "model": {"name": "test"},
- "guard": {"validator": "len(x) > 5", "retry": 1},
- })
- try:
- term = from_config(path)
- assert isinstance(term, Guard)
- finally:
- os.unlink(path)
- def test_schema_validation_missing_type():
- errors = validate_schema({})
- assert any(e[0] == "ERROR" for e in errors)
- def test_lint_no_terminate():
- results = lint_config({
- "type": "react", "systemPrompt": "x",
- "react": {"maxSteps": 10},
- "mcp": {"localTools": []},
- })
- rules = [r.rule for r in results]
- assert any("L004" in r for r in rules)
- def test_lint_clean():
- results = lint_config({
- "type": "react", "systemPrompt": "x",
- "model": {"name": "test"},
- "react": {"maxSteps": 10},
- "mcp": {"localTools": ["terminate"]},
- })
- errors = [r for r in results if r.level == "ERROR"]
- assert len(errors) == 0
- def test_to_lambda_expr():
- expr = to_lambda_expr({"type": "simple", "name": "test", "systemPrompt": "hi"})
- assert "test" in expr
- assert "lambda" in expr
- def test_describe_config():
- desc = describe_config({"type": "react", "name": "agent", "react": {"maxSteps": 10},
- "mcp": {"onlineTool": {"s": ["search"]}, "localTools": ["terminate"]}})
- assert "Y_10" in desc or "Loop" in desc
|