Skip to content

Commit e40f380

Browse files
committed
Refactor LLM service to remove deprecated provider property and update API request structure
1 parent 20de82a commit e40f380

File tree

9 files changed

+179
-62
lines changed

9 files changed

+179
-62
lines changed

memorylayer-core-python/src/memorylayer_server/api/v1/schemas.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@
99

1010
from pydantic import BaseModel, Field
1111

12-
from memorylayer_server.models.association import Association, RelationshipCategory
12+
from memorylayer_server.models.association import (
13+
Association,
14+
GraphPath, # noqa: F401 — re-exported for associations.py
15+
GraphQueryResult, # noqa: F401 — re-exported for associations.py
16+
RelationshipCategory,
17+
)
1318
from memorylayer_server.models.memory import (
1419
Memory,
1520
MemorySubtype,
1621
MemoryType,
1722
RecallMode,
23+
RecallResult, # noqa: F401 — re-exported for memories.py
24+
ReflectResult, # noqa: F401 — re-exported for memories.py
1825
SearchTolerance,
1926
)
2027
from memorylayer_server.models.session import Session, SessionBriefing

memorylayer-core-python/src/memorylayer_server/services/_constants.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
# ============================================
4242
# LLM
4343
# ============================================
44-
EXT_LLM_PROVIDER = "memorylayer-llm-provider"
4544
EXT_LLM_SERVICE = "memorylayer-llm-service"
4645
EXT_LLM_REGISTRY = "memorylayer-llm-registry"
4746

memorylayer-core-python/src/memorylayer_server/services/llm/__init__.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from scitrera_app_framework import Variables, get_extension
44

55
from .base import (
6-
EXT_LLM_PROVIDER,
76
EXT_LLM_REGISTRY,
87
EXT_LLM_SERVICE,
98
LLMProvider,
@@ -20,14 +19,6 @@ def get_llm_registry(v: Variables = None) -> LLMProviderRegistry:
2019
return get_extension(EXT_LLM_REGISTRY, v)
2120

2221

23-
def get_llm_provider(v: Variables = None) -> LLMProvider:
24-
"""Get the default LLM provider instance.
25-
26-
Backward-compatible convenience function that delegates through the registry.
27-
"""
28-
return get_llm_registry(v).get_provider("default")
29-
30-
3122
def get_llm_service(v: Variables = None) -> LLMService:
3223
"""Get the LLM service instance."""
3324
return get_extension(EXT_LLM_SERVICE, v)
@@ -39,10 +30,8 @@ def get_llm_service(v: Variables = None) -> LLMService:
3930
"LLMProviderRegistryPluginBase",
4031
"LLMService",
4132
"LLMServicePluginBase",
42-
"get_llm_provider",
4333
"get_llm_registry",
4434
"get_llm_service",
45-
"EXT_LLM_PROVIDER",
4635
"EXT_LLM_REGISTRY",
4736
"EXT_LLM_SERVICE",
4837
"LLMNotConfiguredError",

memorylayer-core-python/src/memorylayer_server/services/llm/service_default.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from scitrera_app_framework import Variables, get_logger
77

88
from ...models.llm import LLMMessage, LLMRequest, LLMResponse, LLMRole, LLMStreamChunk
9-
from .base import EXT_LLM_REGISTRY, LLMProvider, LLMServicePluginBase
9+
from .base import EXT_LLM_REGISTRY, LLMServicePluginBase
1010
from .registry import LLMProviderRegistry
1111

1212

@@ -22,11 +22,6 @@ def __init__(self, registry: LLMProviderRegistry, v: Variables = None):
2222
self.registry = registry
2323
self.logger = get_logger(v, name=self.__class__.__name__)
2424

25-
@property
26-
def provider(self) -> LLMProvider:
27-
"""Default provider for backward compatibility."""
28-
return self.registry.get_provider("default")
29-
3025
async def complete(self, request: LLMRequest, profile: str = "default") -> LLMResponse:
3126
"""Route completion to the provider for the given profile."""
3227
return await self.registry.complete(request, profile=profile)
@@ -122,13 +117,13 @@ async def answer_question(
122117

123118
@property
124119
def default_model(self) -> str:
125-
"""Default model from provider."""
126-
return self.provider.default_model
120+
"""Default model from the default profile provider."""
121+
return self.registry.get_provider("default").default_model
127122

128123
@property
129124
def supports_streaming(self) -> bool:
130-
"""Streaming support from provider."""
131-
return self.provider.supports_streaming
125+
"""Streaming support from the default profile provider."""
126+
return self.registry.get_provider("default").supports_streaming
132127

133128

134129
class DefaultLLMServicePlugin(LLMServicePluginBase):

memorylayer-core-python/src/memorylayer_server/services/tasks/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from ...config import DEFAULT_MEMORYLAYER_TASK_PROVIDER, MEMORYLAYER_TASK_PROVIDER
1515
from .._constants import (
16+
EXT_MULTI_TASK_HANDLERS, # noqa: F401 — re-exported for handlers.py and __init__.py
1617
EXT_STORAGE_BACKEND,
1718
EXT_TASK_SERVICE,
1819
)

memorylayer-core-python/tests/integration/test_memories_api.py

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -327,18 +327,14 @@ def test_batch_operations(self, test_client: TestClient, workspace_headers: dict
327327
json={
328328
"operations": [
329329
{
330-
"type": "create",
331-
"data": {
332-
"content": "First batch memory",
333-
"importance": 0.5,
334-
},
330+
"op": "create",
331+
"content": "First batch memory",
332+
"importance": 0.5,
335333
},
336334
{
337-
"type": "create",
338-
"data": {
339-
"content": "Second batch memory",
340-
"importance": 0.6,
341-
},
335+
"op": "create",
336+
"content": "Second batch memory",
337+
"importance": 0.6,
342338
},
343339
]
344340
},
@@ -369,25 +365,19 @@ def test_batch_operations_mixed_types(self, test_client: TestClient, workspace_h
369365
json={
370366
"operations": [
371367
{
372-
"type": "create",
373-
"data": {
374-
"content": "New batch memory",
375-
},
368+
"op": "create",
369+
"content": "New batch memory",
376370
},
377371
{
378-
"type": "update",
379-
"data": {
380-
"memory_id": memory_id,
381-
"content": "Updated content",
382-
"importance": 0.9,
383-
},
372+
"op": "update",
373+
"memory_id": memory_id,
374+
"content": "Updated content",
375+
"importance": 0.9,
384376
},
385377
{
386-
"type": "delete",
387-
"data": {
388-
"memory_id": memory_id,
389-
"hard": False,
390-
},
378+
"op": "delete",
379+
"memory_id": memory_id,
380+
"hard": False,
391381
},
392382
]
393383
},

memorylayer-core-python/tests/unit/test_llm_registry.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -477,13 +477,14 @@ async def test_default_profile_when_not_specified(self):
477477
default.complete.assert_called_once_with(request)
478478
cheap.complete.assert_not_called()
479479

480-
def test_provider_property_returns_default(self):
481-
"""service.provider returns default provider from registry."""
480+
def test_default_model_uses_default_profile(self):
481+
"""service.default_model delegates to default profile provider."""
482482
default = _mock_provider("default")
483+
default.default_model = "gpt-4o"
483484
cheap = _mock_provider("cheap")
484485
registry = LLMProviderRegistry(
485486
providers={"default": default, "cheap": cheap},
486487
)
487488
service = LLMService(registry=registry)
488489

489-
assert service.provider is default
490+
assert service.default_model == "gpt-4o"

memorylayer-sdk-python/tests/test_sync_client.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -283,18 +283,20 @@ def test_session_context(client: SyncMemoryLayerClient, base_url: str) -> None:
283283
@respx.mock
284284
def test_sync_client_helper(base_url: str) -> None:
285285
"""Test sync_client() context manager helper."""
286-
# Mock response
286+
# Mock response (server wraps in MemoryResponse envelope)
287287
mock_response = {
288-
"id": "mem_456",
289-
"workspace_id": "ws_test",
290-
"content": "Test memory",
291-
"type": "working",
292-
"importance": 0.5,
293-
"tags": [],
294-
"metadata": {},
295-
"access_count": 0,
296-
"created_at": "2026-01-26T10:00:00Z",
297-
"updated_at": "2026-01-26T10:00:00Z",
288+
"memory": {
289+
"id": "mem_456",
290+
"workspace_id": "ws_test",
291+
"content": "Test memory",
292+
"type": "working",
293+
"importance": 0.5,
294+
"tags": [],
295+
"metadata": {},
296+
"access_count": 0,
297+
"created_at": "2026-01-26T10:00:00Z",
298+
"updated_at": "2026-01-26T10:00:00Z",
299+
}
298300
}
299301

300302
respx.post(f"{base_url}/v1/memories").mock(return_value=Response(200, json=mock_response))

scripts/ci-local.sh

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/usr/bin/env bash
2+
# Local CI — mirrors .github/workflows/ci.yml
3+
# Usage: ./scripts/ci-local.sh [--fix] [component...]
4+
# --fix Auto-fix lint/format issues instead of just checking
5+
# component One or more of: server, sdk, typescript (default: all)
6+
#
7+
# Examples:
8+
# ./scripts/ci-local.sh # Run everything
9+
# ./scripts/ci-local.sh server # Server only
10+
# ./scripts/ci-local.sh sdk --fix # SDK with auto-fix
11+
# ./scripts/ci-local.sh server sdk # Server + SDK, skip TypeScript
12+
13+
set -euo pipefail
14+
cd "$(dirname "$0")/.."
15+
16+
RED='\033[0;31m'
17+
GREEN='\033[0;32m'
18+
YELLOW='\033[1;33m'
19+
BOLD='\033[1m'
20+
NC='\033[0m'
21+
22+
FIX=false
23+
COMPONENTS=()
24+
25+
for arg in "$@"; do
26+
case "$arg" in
27+
--fix) FIX=true ;;
28+
server|sdk|typescript|ts) COMPONENTS+=("$arg") ;;
29+
*) echo -e "${RED}Unknown argument: $arg${NC}"; exit 1 ;;
30+
esac
31+
done
32+
33+
# Default: run all
34+
if [ ${#COMPONENTS[@]} -eq 0 ]; then
35+
COMPONENTS=(server sdk typescript)
36+
fi
37+
38+
FAILURES=()
39+
40+
run_step() {
41+
local label="$1"
42+
shift
43+
echo -e "${BOLD}$label${NC}"
44+
if "$@"; then
45+
echo -e " ${GREEN}✓ passed${NC}"
46+
else
47+
echo -e " ${RED}✗ failed${NC}"
48+
FAILURES+=("$label")
49+
fi
50+
}
51+
52+
# ──────────────────────────────────────────────────────────────────
53+
# Python server (memorylayer-core-python)
54+
# ──────────────────────────────────────────────────────────────────
55+
run_server() {
56+
echo -e "\n${YELLOW}━━━ Python: memorylayer-server ━━━${NC}"
57+
local dir="memorylayer-core-python"
58+
59+
if [ "$FIX" = true ]; then
60+
run_step "server: ruff fix" python3 -m ruff check --fix --unsafe-fixes "$dir"
61+
run_step "server: ruff format" python3 -m ruff format "$dir"
62+
else
63+
run_step "server: ruff check" python3 -m ruff check "$dir"
64+
run_step "server: ruff format" python3 -m ruff format --check "$dir"
65+
fi
66+
67+
if [ -d "$dir/.venv" ]; then
68+
(cd "$dir" && source .venv/bin/activate && run_step "server: pytest" python3 -m pytest tests/ -m "not slow and not integration and not llm and not llm_quality" -x -q)
69+
else
70+
echo -e " ${RED}No .venv found in $dir — run: cd $dir && python3 -m venv .venv && pip install -e '.[dev]'${NC}"
71+
FAILURES+=("server: pytest (no venv)")
72+
fi
73+
}
74+
75+
# ──────────────────────────────────────────────────────────────────
76+
# Python SDK (memorylayer-sdk-python)
77+
# ──────────────────────────────────────────────────────────────────
78+
run_sdk() {
79+
echo -e "\n${YELLOW}━━━ Python: memorylayer-client ━━━${NC}"
80+
local dir="memorylayer-sdk-python"
81+
82+
if [ "$FIX" = true ]; then
83+
run_step "sdk: ruff fix" python3 -m ruff check --fix --unsafe-fixes "$dir"
84+
run_step "sdk: ruff format" python3 -m ruff format "$dir"
85+
else
86+
run_step "sdk: ruff check" python3 -m ruff check "$dir"
87+
run_step "sdk: ruff format" python3 -m ruff format --check "$dir"
88+
fi
89+
90+
if [ -d "$dir/.venv" ]; then
91+
(cd "$dir" && source .venv/bin/activate && run_step "sdk: pytest" python3 -m pytest tests/ -x -q)
92+
else
93+
echo -e " ${RED}No .venv found in $dir — run: cd $dir && python3 -m venv .venv && pip install -e '.[dev]'${NC}"
94+
FAILURES+=("sdk: pytest (no venv)")
95+
fi
96+
}
97+
98+
# ──────────────────────────────────────────────────────────────────
99+
# TypeScript packages
100+
# ──────────────────────────────────────────────────────────────────
101+
run_typescript() {
102+
echo -e "\n${YELLOW}━━━ TypeScript: memorylayer-sdk ━━━${NC}"
103+
(cd memorylayer-sdk-typescript && run_step "ts-sdk: npm ci" npm ci && run_step "ts-sdk: build" npm run build)
104+
105+
echo -e "\n${YELLOW}━━━ TypeScript: memorylayer-mcp-server ━━━${NC}"
106+
(cd memorylayer-mcp-typescript && run_step "ts-mcp: npm ci" npm ci && run_step "ts-mcp: build" npm run build)
107+
}
108+
109+
# ──────────────────────────────────────────────────────────────────
110+
# Run selected components
111+
# ──────────────────────────────────────────────────────────────────
112+
for component in "${COMPONENTS[@]}"; do
113+
case "$component" in
114+
server) run_server ;;
115+
sdk) run_sdk ;;
116+
typescript|ts) run_typescript ;;
117+
esac
118+
done
119+
120+
# ──────────────────────────────────────────────────────────────────
121+
# Summary
122+
# ──────────────────────────────────────────────────────────────────
123+
echo ""
124+
if [ ${#FAILURES[@]} -eq 0 ]; then
125+
echo -e "${GREEN}${BOLD}All checks passed!${NC}"
126+
exit 0
127+
else
128+
echo -e "${RED}${BOLD}${#FAILURES[@]} check(s) failed:${NC}"
129+
for f in "${FAILURES[@]}"; do
130+
echo -e " ${RED}$f${NC}"
131+
done
132+
exit 1
133+
fi

0 commit comments

Comments
 (0)