feat: add emulated tool-calling bridge for Lingma
Add a proxy-side tool emulation layer so Lingma requests can surface stable OpenAI tool_calls and Anthropic tool_use blocks even when upstream tool events are missing or inconsistent. Constraint: Keep native Lingma tool event bridging as the first path and layer emulation as a fallback Rejected: Depend exclusively on Lingma native tool/invoke events | tool visibility remains inconsistent across models and transports Confidence: high Scope-risk: moderate
This commit is contained in:
@@ -3,10 +3,12 @@ from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import types
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
import zipfile
|
||||
|
||||
# app.lingma_pool imports auto_login; tests here don't execute Playwright paths.
|
||||
# Stub module import so test environments without playwright can import pool code.
|
||||
@@ -28,6 +30,7 @@ sys.modules.setdefault("playwright", _playwright)
|
||||
sys.modules.setdefault("playwright.async_api", _playwright_async)
|
||||
|
||||
from app.config import _parse_accounts, load_settings
|
||||
from app.bootstrap_lingma import bootstrap_from_vsix
|
||||
from app.lingma_pool import LingmaPool
|
||||
from app.stats import StatsCollector, estimate_tokens
|
||||
|
||||
@@ -212,5 +215,57 @@ class ConfigParsingTests(unittest.TestCase):
|
||||
self.assertEqual(settings.tool_allowlist, [])
|
||||
|
||||
|
||||
class BootstrapLingmaTests(unittest.TestCase):
|
||||
def _make_test_vsix(self, root: str) -> str:
|
||||
nested_zip_path = os.path.join(root, "nested.zip")
|
||||
with zipfile.ZipFile(nested_zip_path, "w") as nested:
|
||||
nested.writestr("2.5.20/x86_64_linux/Lingma", b"new-binary")
|
||||
nested.writestr("2.5.20/extension/main.js", b"console.log('ok')")
|
||||
|
||||
vsix_path = os.path.join(root, "test.vsix")
|
||||
with zipfile.ZipFile(vsix_path, "w") as vsix:
|
||||
with open(nested_zip_path, "rb") as nested_file:
|
||||
vsix.writestr(
|
||||
"extension/dist/bin/lingma-2.5.20.zip",
|
||||
nested_file.read(),
|
||||
)
|
||||
return vsix_path
|
||||
|
||||
def test_bootstrap_refreshes_when_extension_assets_missing(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
bin_dir = os.path.join(tmpdir, "data", "bin")
|
||||
release_dir = os.path.join(bin_dir, "2.5.20")
|
||||
os.makedirs(release_dir, exist_ok=True)
|
||||
|
||||
lingma_bin = os.path.join(bin_dir, "Lingma")
|
||||
with open(lingma_bin, "wb") as f:
|
||||
f.write(b"old-binary")
|
||||
|
||||
marker = {
|
||||
"version": "2.5.20",
|
||||
"release_root": "2.5.20",
|
||||
}
|
||||
with open(os.path.join(bin_dir, ".lingma-bootstrap.json"), "w", encoding="utf-8") as f:
|
||||
json.dump(marker, f)
|
||||
|
||||
vsix_path = self._make_test_vsix(tmpdir)
|
||||
|
||||
env = {
|
||||
"LINGMA_BIN": lingma_bin,
|
||||
"LINGMA_SOURCE_TYPE": "vsix",
|
||||
"LINGMA_VSIX_URL": f"file://{vsix_path}",
|
||||
"LINGMA_BOOTSTRAP_ALWAYS": "false",
|
||||
"LINGMA_FORCE_REFRESH": "false",
|
||||
}
|
||||
with patch.dict(os.environ, env, clear=False):
|
||||
bootstrap_from_vsix()
|
||||
|
||||
with open(lingma_bin, "rb") as f:
|
||||
self.assertEqual(f.read(), b"new-binary")
|
||||
self.assertTrue(
|
||||
os.path.exists(os.path.join(release_dir, "extension", "main.js"))
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user