Skip to content
Closed
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
2 changes: 2 additions & 0 deletions src/spark_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def discover_repo_root() -> Path:
"/root",
"/var/run/docker.sock",
)
OPENAI_COMPAT_HTTP_USER_AGENT = "Spark-CLI/1.0 (+https://github.com/vibeforge1111/spark-cli)"
TRUST_TIERS = ("builtin", "trusted", "community", "untrusted")
TRUST_BLOCK_THRESHOLD = {
"builtin": "critical",
Expand Down Expand Up @@ -10409,6 +10410,7 @@ def openai_compatible_chat_completion(target: dict[str, Any], prompt: str) -> st
headers={
"Authorization": f"Bearer {target['api_key']}",
"Content-Type": "application/json",
"User-Agent": OPENAI_COMPAT_HTTP_USER_AGENT,
},
method="POST",
)
Expand Down
31 changes: 30 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import tempfile
import unittest
import urllib.error
import urllib.request
from argparse import Namespace
from contextlib import redirect_stdout
from io import StringIO
Expand Down Expand Up @@ -148,7 +149,8 @@
step_previously_completed,
manifest_schema_version,
manual_telegram_profiles,
needs_capabilities,
openai_compatible_chat_completion,
OPENAI_COMPAT_HTTP_USER_AGENT,
parse_secret_pairs,
parse_version_constraint,
parse_version_tuple,
Expand Down Expand Up @@ -9958,6 +9960,33 @@ def test_provider_status_payload_uses_legacy_secret_keys_for_api_auth(self) -> N
self.assertEqual(payload["roles"][role]["auth_mode"], "api_key")
self.assertEqual(payload["roles"][role]["model"], "glm-5.1")

def test_openai_compatible_chat_completion_sends_user_agent(self) -> None:
captured: dict[str, str] = {}

class FakeResponse:
def read(self) -> bytes:
return json.dumps({"choices": [{"message": {"content": "PING_OK"}}]}).encode("utf-8")

def __enter__(self):
return self

def __exit__(self, *args: object) -> None:
return None

def fake_urlopen(request: urllib.request.Request, timeout: float = 0) -> FakeResponse:
captured["User-Agent"] = request.headers.get("User-agent") or request.headers.get("User-Agent", "")
return FakeResponse()

target = {
"base_url": "https://api.example.test/v1",
"api_key": "test-key",
"model": "test-model",
}
with patch("urllib.request.urlopen", side_effect=fake_urlopen):
result = openai_compatible_chat_completion(target, "ping")
self.assertEqual(result, "PING_OK")
self.assertEqual(captured["User-Agent"], OPENAI_COMPAT_HTTP_USER_AGENT)

def test_collect_verify_payload_reports_launch_ready_stack(self) -> None:
expected = [
"spark-researcher",
Expand Down