Skip to content

Commit 305fd31

Browse files
ryaneggzclaude
andcommitted
fix: restore pre-commit hooks and fix all backend lint/test failures
Removed bd (beads) tool that had overwritten git hooks. Installed pre-commit framework with 6 hooks: backend format/lint/test and frontend prettier/lint/test. Fixed 100+ ruff lint errors (F401, F403, F405, F811, F821, E501), configured ruff in pyproject.toml, and resolved all 17 test failures (auth mocking, langchain API compat, conditional enum guards). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 06a275a commit 305fd31

153 files changed

Lines changed: 962 additions & 2299 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ repos:
88
files: ^backend/
99
pass_filenames: false
1010
always_run: false
11+
- id: backend-lint
12+
name: Backend Lint
13+
entry: bash -c 'cd backend && make lint'
14+
language: system
15+
files: ^backend/
16+
pass_filenames: false
17+
always_run: false
18+
- id: backend-test
19+
name: Backend Test
20+
entry: bash -c 'cd backend && make test ENV_FILE=~/.env/orchestra/.env.backend.test'
21+
language: system
22+
files: ^backend/
23+
pass_filenames: false
24+
always_run: false
1125
- id: frontend-prettier
1226
name: Frontend Prettier
1327
entry: npx prettier --write
@@ -22,3 +36,10 @@ repos:
2236
files: ^frontend/
2337
pass_filenames: false
2438
always_run: false
39+
- id: frontend-test
40+
name: Frontend Test
41+
entry: bash -c 'cd frontend && npm run test'
42+
language: system
43+
files: ^frontend/
44+
pass_filenames: false
45+
always_run: false

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: update-submodules ralph archive
1+
.PHONY: update-submodules ralph archive setup
22

33
ENV ?= dev
44
MAX_ITERATIONS ?= 200
@@ -18,6 +18,10 @@ update-submodules:
1818

1919
@echo "🏁 Done."
2020

21+
# Install pre-commit hooks
22+
setup:
23+
pre-commit install
24+
2125
# Run the Ralph autonomous agent loop using Claude Code
2226
ralph:
2327
bash .ralph/ralph.sh $(MAX_ITERATIONS)

backend/Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: dev claude build run tag seeds test format
1+
.PHONY: dev claude build run tag seeds test format lint
22

33
ENV_FILE ?= $(HOME)/.env/orchestra/.env.backend
44

@@ -23,6 +23,9 @@ seeds.user:
2323
format:
2424
uvx ruff format
2525

26+
lint:
27+
uvx ruff check
28+
2629
test:
2730
@set -a && . $(ENV_FILE) && set +a && uv run pytest
2831

backend/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
async def lifespan(app: FastAPI):
5959
# Startup
6060
print("Starting up...")
61-
print(f"Environment Settings:")
61+
print("Environment Settings:")
6262
print(f"APP_VERSION: {APP_VERSION}")
6363
print(f"LOG_LEVEL: {LOG_LEVEL}")
6464
print(f"HOST: {HOST}")
@@ -100,7 +100,7 @@ async def lifespan(app: FastAPI):
100100
"This is a simple API for building chatbots with LangGraph. "
101101
"It allows you to create new threads, query existing threads, "
102102
"and get the history of a thread.\n Check out the repo on "
103-
f"<a href='https://github.com/ruska-ai/orchestra'>Github</a>"
103+
"<a href='https://github.com/ruska-ai/orchestra'>Github</a>"
104104
),
105105
contact={"name": "Ryan Eggleston", "email": "reggleston@ruska.ai"},
106106
debug=True,

backend/migrations/env.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ async def ensure_database_exists(db_uri: str) -> None:
3939

4040
# Connect to the default 'postgres' database to create the target db
4141
# Convert to asyncpg format for async engine
42-
postgres_uri = f"{base_uri}/postgres".replace(
43-
"postgresql://", "postgresql+asyncpg://"
44-
)
42+
postgres_uri = f"{base_uri}/postgres".replace("postgresql://", "postgresql+asyncpg://")
4543

4644
try:
4745
engine = create_async_engine(

backend/pyproject.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ dev = [
7676
"respx>=0.22.0"
7777
]
7878

79+
[tool.ruff]
80+
line-length = 120
81+
82+
[tool.ruff.lint]
83+
select = ["E", "F"]
84+
85+
[tool.ruff.lint.per-file-ignores]
86+
"main.py" = ["E402"]
87+
"migrations/env.py" = ["E402"]
88+
"src/constants/llm.py" = ["E402"]
89+
"src/tools/code.py" = ["E402"]
90+
"src/utils/logger.py" = ["E402"]
91+
"src/services/checkpoint.py" = ["E402"]
92+
"src/constants/mock.py" = ["E501"]
93+
7994
[tool.pytest.ini_options]
8095
pythonpath = ["."]
8196
asyncio_default_fixture_loop_scope = "session"

backend/seeds/user_seeder.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@
3434
async def seed_admin():
3535
async with AsyncSessionLocal() as db:
3636
try:
37-
result = await db.execute(
38-
select(User).filter(User.email == "admin@example.com")
39-
)
37+
result = await db.execute(select(User).filter(User.email == "admin@example.com"))
4038
admin = result.scalar_one_or_none()
4139
if admin:
4240
print("Admin exists, skipping seeding")
@@ -58,9 +56,7 @@ async def seed_admin():
5856
async def seed_user():
5957
async with AsyncSessionLocal() as db:
6058
try:
61-
result = await db.execute(
62-
select(User).filter(User.email == "user@example.com")
63-
)
59+
result = await db.execute(select(User).filter(User.email == "user@example.com"))
6460
user = result.scalar_one_or_none()
6561
if user:
6662
print("User exists, skipping seeding")

backend/src/agents/__init__.py

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from langchain_core.language_models import BaseChatModel
99
from langchain_core.tools import BaseTool
1010
from langgraph.checkpoint.base import BaseCheckpointSaver
11-
from langchain.agents import create_agent
1211
from langgraph.store.base import BaseStore
1312
from langchain_core.messages import BaseMessage
1413
from langgraph.graph.state import CompiledStateGraph
@@ -56,11 +55,7 @@ def memory_to_xml(memory):
5655
items.append(f"<{key}>{value}</{key}>")
5756
return f"<memory>{''.join(items)}</memory>"
5857

59-
formatted_memories = (
60-
"\n".join(memory_to_xml(memory) for memory in memories)
61-
if memories
62-
else "No memories found."
63-
)
58+
formatted_memories = "\n".join(memory_to_xml(memory) for memory in memories) if memories else "No memories found."
6459

6560
return (
6661
"You have the following general memories "
@@ -173,9 +168,7 @@ async def init_tools(
173168
tools_list = tools_list + await mcp_client.get_tools()
174169
if user_id:
175170
for tool in tools:
176-
items = await service_context.tool_service.tool_repo.search(
177-
filter={"name": tool}
178-
)
171+
items = await service_context.tool_service.tool_repo.search(filter={"name": tool})
179172
if items:
180173
structured_tool = items[0]
181174
tool_metadata = {structured_tool.name: structured_tool.metadata}
@@ -187,21 +180,15 @@ async def init_tools(
187180
return tools_list
188181

189182

190-
async def init_subagents(
191-
subagents: list[Assistant], service_context: ServiceContext
192-
) -> list[SubAgent]:
183+
async def init_subagents(subagents: list[Assistant], service_context: ServiceContext) -> list[SubAgent]:
193184
result = []
194185
for subagent in subagents:
195-
system_prompt = subagent.system_prompt or init_system_prompt(
196-
DEFAULT_SYSTEM_PROMPT, {}, subagent.instructions
197-
)
186+
system_prompt = subagent.system_prompt or init_system_prompt(DEFAULT_SYSTEM_PROMPT, {}, subagent.instructions)
198187
subagent_dict = {
199188
"name": subagent.slug,
200189
"description": subagent.description,
201190
"system_prompt": system_prompt,
202-
"tools": await init_tools(
203-
subagent.tools, subagent.a2a, subagent.mcp, service_context
204-
),
191+
"tools": await init_tools(subagent.tools, subagent.a2a, subagent.mcp, service_context),
205192
}
206193

207194
if getattr(subagent, "model", None) is not None:
@@ -375,9 +362,7 @@ async def construct_agent(
375362
model=model,
376363
tools=tools,
377364
subagents=subagents,
378-
system_prompt=init_system_prompt(
379-
system_prompt, service_context.config or {}, instructions
380-
),
365+
system_prompt=init_system_prompt(system_prompt, service_context.config or {}, instructions),
381366
checkpointer=checkpointer,
382367
store=service_context.store,
383368
middleware=middleware,
@@ -449,6 +434,4 @@ def astream(
449434
config: RunnableConfig = None,
450435
context: dict[str, Any] = None,
451436
) -> AsyncGenerator[BaseMessage, None]:
452-
return self.graph.astream(
453-
messages, config=config, stream_mode=stream_mode, context=context
454-
)
437+
return self.graph.astream(messages, config=config, stream_mode=stream_mode, context=context)

backend/src/common/client/client.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,10 @@ async def send_task(self, payload: dict[str, Any]) -> SendTaskResponse:
3535
request = SendTaskRequest(params=payload)
3636
return SendTaskResponse(**await self._send_request(request))
3737

38-
async def send_task_streaming(
39-
self, payload: dict[str, Any]
40-
) -> AsyncIterable[SendTaskStreamingResponse]:
38+
async def send_task_streaming(self, payload: dict[str, Any]) -> AsyncIterable[SendTaskStreamingResponse]:
4139
request = SendTaskStreamingRequest(params=payload)
4240
with httpx.Client(timeout=None) as client:
43-
with connect_sse(
44-
client, "POST", self.url, json=request.model_dump()
45-
) as event_source:
41+
with connect_sse(client, "POST", self.url, json=request.model_dump()) as event_source:
4642
try:
4743
for sse in event_source.iter_sse():
4844
yield SendTaskStreamingResponse(**json.loads(sse.data))
@@ -55,9 +51,7 @@ async def _send_request(self, request: JSONRPCRequest) -> dict[str, Any]:
5551
async with httpx.AsyncClient() as client:
5652
try:
5753
# Image generation could take time, adding timeout
58-
response = await client.post(
59-
self.url, json=request.model_dump(), timeout=30
60-
)
54+
response = await client.post(self.url, json=request.model_dump(), timeout=30)
6155
response.raise_for_status()
6256
return response.json()
6357
except httpx.HTTPStatusError as e:
@@ -73,14 +67,10 @@ async def cancel_task(self, payload: dict[str, Any]) -> CancelTaskResponse:
7367
request = CancelTaskRequest(params=payload)
7468
return CancelTaskResponse(**await self._send_request(request))
7569

76-
async def set_task_callback(
77-
self, payload: dict[str, Any]
78-
) -> SetTaskPushNotificationResponse:
70+
async def set_task_callback(self, payload: dict[str, Any]) -> SetTaskPushNotificationResponse:
7971
request = SetTaskPushNotificationRequest(params=payload)
8072
return SetTaskPushNotificationResponse(**await self._send_request(request))
8173

82-
async def get_task_callback(
83-
self, payload: dict[str, Any]
84-
) -> GetTaskPushNotificationResponse:
74+
async def get_task_callback(self, payload: dict[str, Any]) -> GetTaskPushNotificationResponse:
8575
request = GetTaskPushNotificationRequest(params=payload)
8676
return GetTaskPushNotificationResponse(**await self._send_request(request))

backend/src/common/server/server.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ def __init__(
4343
self.agent_card = agent_card
4444
self.app = Starlette()
4545
self.app.add_route(self.endpoint, self._process_request, methods=["POST"])
46-
self.app.add_route(
47-
"/.well-known/agent.json", self._get_agent_card, methods=["GET"]
48-
)
46+
self.app.add_route("/.well-known/agent.json", self._get_agent_card, methods=["GET"])
4947

5048
def start(self):
5149
if self.agent_card is None:
@@ -71,23 +69,15 @@ async def _process_request(self, request: Request):
7169
elif isinstance(json_rpc_request, SendTaskRequest):
7270
result = await self.task_manager.on_send_task(json_rpc_request)
7371
elif isinstance(json_rpc_request, SendTaskStreamingRequest):
74-
result = await self.task_manager.on_send_task_subscribe(
75-
json_rpc_request
76-
)
72+
result = await self.task_manager.on_send_task_subscribe(json_rpc_request)
7773
elif isinstance(json_rpc_request, CancelTaskRequest):
7874
result = await self.task_manager.on_cancel_task(json_rpc_request)
7975
elif isinstance(json_rpc_request, SetTaskPushNotificationRequest):
80-
result = await self.task_manager.on_set_task_push_notification(
81-
json_rpc_request
82-
)
76+
result = await self.task_manager.on_set_task_push_notification(json_rpc_request)
8377
elif isinstance(json_rpc_request, GetTaskPushNotificationRequest):
84-
result = await self.task_manager.on_get_task_push_notification(
85-
json_rpc_request
86-
)
78+
result = await self.task_manager.on_get_task_push_notification(json_rpc_request)
8779
elif isinstance(json_rpc_request, TaskResubscriptionRequest):
88-
result = await self.task_manager.on_resubscribe_to_task(
89-
json_rpc_request
90-
)
80+
result = await self.task_manager.on_resubscribe_to_task(json_rpc_request)
9181
else:
9282
logger.warning(f"Unexpected request type: {type(json_rpc_request)}")
9383
raise ValueError(f"Unexpected request type: {type(request)}")

0 commit comments

Comments
 (0)