generated from coulomb/repo-seed
Add activity-core LLM endpoint support
This commit is contained in:
@@ -17,7 +17,9 @@ from llm_connect._diagnostics import (
|
||||
record_provider_response,
|
||||
)
|
||||
from llm_connect.adapter import MockLLMAdapter, ErrorLLMAdapter
|
||||
from llm_connect.exceptions import LLMAPIError, LLMConfigurationError, LLMTimeoutError
|
||||
from llm_connect.models import LLMResponse, RunConfig
|
||||
from llm_connect.profiles import CUSTODIAN_TRIAGE_BALANCED, ProfiledLLMAdapter, RuntimeProfile
|
||||
from llm_connect.server import LLMServer
|
||||
|
||||
|
||||
@@ -151,7 +153,8 @@ class TestExecute:
|
||||
{"prompt": "hello"},
|
||||
)
|
||||
assert status == 500
|
||||
assert "boom" in body["error"]
|
||||
assert body["error"] == "internal_error"
|
||||
assert "boom" in body["message"]
|
||||
finally:
|
||||
s.stop()
|
||||
|
||||
@@ -189,6 +192,142 @@ class TestExecute:
|
||||
assert status == 400
|
||||
assert "config" in body["error"]
|
||||
|
||||
def test_profile_execute_resolves_model_and_metadata(self):
|
||||
created: list[MockLLMAdapter] = []
|
||||
|
||||
def factory(provider: str, model: str) -> MockLLMAdapter:
|
||||
created.append(MockLLMAdapter(mock_response="profile response"))
|
||||
return created[-1]
|
||||
|
||||
adapter = ProfiledLLMAdapter(
|
||||
MockLLMAdapter(mock_response="default"),
|
||||
{
|
||||
CUSTODIAN_TRIAGE_BALANCED: RuntimeProfile(
|
||||
name=CUSTODIAN_TRIAGE_BALANCED,
|
||||
provider="mock",
|
||||
model="triage-model",
|
||||
config=RunConfig(
|
||||
model_name="triage-model",
|
||||
temperature=0.2,
|
||||
max_tokens=1800,
|
||||
max_depth=2,
|
||||
model_params={"reasoning_effort": "medium"},
|
||||
),
|
||||
)
|
||||
},
|
||||
adapter_factory=factory,
|
||||
)
|
||||
s = LLMServer(adapter=adapter, port=0)
|
||||
s.start()
|
||||
try:
|
||||
status, body = _post(
|
||||
f"http://127.0.0.1:{s.port}/execute",
|
||||
{
|
||||
"prompt": "Return JSON.",
|
||||
"config": {
|
||||
"model_name": CUSTODIAN_TRIAGE_BALANCED,
|
||||
"model_params": {"json_schema": {"type": "object"}},
|
||||
},
|
||||
},
|
||||
)
|
||||
finally:
|
||||
s.stop()
|
||||
|
||||
assert status == 200
|
||||
assert body["model"] == "triage-model"
|
||||
assert body["metadata"]["profile"] == CUSTODIAN_TRIAGE_BALANCED
|
||||
assert body["metadata"]["profile_provider"] == "mock"
|
||||
assert len(created) == 1
|
||||
assert created[0].last_config.model_name == "triage-model"
|
||||
assert created[0].last_config.temperature == 0.2
|
||||
assert created[0].last_config.max_tokens == 1800
|
||||
assert created[0].last_config.max_depth == 2
|
||||
assert created[0].last_config.model_params == {
|
||||
"reasoning_effort": "medium",
|
||||
"json_schema": {"type": "object"},
|
||||
}
|
||||
|
||||
def test_unknown_profile_returns_400(self):
|
||||
s = LLMServer(adapter=ProfiledLLMAdapter(MockLLMAdapter(), {}), port=0)
|
||||
s.start()
|
||||
try:
|
||||
status, body = _post(
|
||||
f"http://127.0.0.1:{s.port}/execute",
|
||||
{"prompt": "hello", "config": {"model_name": "custodian-missing"}},
|
||||
)
|
||||
finally:
|
||||
s.stop()
|
||||
|
||||
assert status == 400
|
||||
assert body["error"] == "unknown_profile"
|
||||
assert body["context"]["profile"] == "custodian-missing"
|
||||
|
||||
def test_configuration_error_is_sanitized(self):
|
||||
class SecretConfigAdapter(MockLLMAdapter):
|
||||
def execute_prompt(self, prompt: str, config: RunConfig) -> LLMResponse:
|
||||
raise LLMConfigurationError(
|
||||
"Bad api_key=sk-supersecret with Bearer secret-token",
|
||||
context={"api_key": "sk-supersecret", "provider": "openai"},
|
||||
)
|
||||
|
||||
s = LLMServer(adapter=SecretConfigAdapter(), port=0)
|
||||
s.start()
|
||||
try:
|
||||
status, body = _post(
|
||||
f"http://127.0.0.1:{s.port}/execute",
|
||||
{"prompt": "hello"},
|
||||
)
|
||||
finally:
|
||||
s.stop()
|
||||
|
||||
assert status == 500
|
||||
assert body["error"] == "configuration_error"
|
||||
assert "sk-supersecret" not in json.dumps(body)
|
||||
assert "secret-token" not in json.dumps(body)
|
||||
assert body["context"]["api_key"] == "<redacted>"
|
||||
assert body["context"]["provider"] == "openai"
|
||||
|
||||
def test_provider_errors_are_categorized_and_sanitized(self):
|
||||
class ProviderErrorAdapter(MockLLMAdapter):
|
||||
def execute_prompt(self, prompt: str, config: RunConfig) -> LLMResponse:
|
||||
raise LLMAPIError(
|
||||
"HTTP 500 from https://provider.example/v1?key=gemini-secret",
|
||||
status_code=500,
|
||||
)
|
||||
|
||||
s = LLMServer(adapter=ProviderErrorAdapter(), port=0)
|
||||
s.start()
|
||||
try:
|
||||
status, body = _post(
|
||||
f"http://127.0.0.1:{s.port}/execute",
|
||||
{"prompt": "hello"},
|
||||
)
|
||||
finally:
|
||||
s.stop()
|
||||
|
||||
assert status == 502
|
||||
assert body["error"] == "provider_api_error"
|
||||
assert body["provider_status"] == 500
|
||||
assert "gemini-secret" not in body["message"]
|
||||
|
||||
def test_timeout_error_returns_504(self):
|
||||
class TimeoutAdapter(MockLLMAdapter):
|
||||
def execute_prompt(self, prompt: str, config: RunConfig) -> LLMResponse:
|
||||
raise LLMTimeoutError("Request timed out after 300s")
|
||||
|
||||
s = LLMServer(adapter=TimeoutAdapter(), port=0)
|
||||
s.start()
|
||||
try:
|
||||
status, body = _post(
|
||||
f"http://127.0.0.1:{s.port}/execute",
|
||||
{"prompt": "hello"},
|
||||
)
|
||||
finally:
|
||||
s.stop()
|
||||
|
||||
assert status == 504
|
||||
assert body["error"] == "provider_timeout"
|
||||
|
||||
def test_debug_query_returns_diagnostics(self):
|
||||
s = LLMServer(adapter=DiagnosticLLMAdapter(mock_response="debug body"), port=0)
|
||||
s.start()
|
||||
|
||||
Reference in New Issue
Block a user