From f4427f38fe3e9aa6a373a9214b6947b9dc5e2b6f Mon Sep 17 00:00:00 2001 From: Aditi Mehta Date: Thu, 25 Jun 2026 16:44:29 +0530 Subject: [PATCH 1/2] feat(signals): implement dynamic github event ingestion and goal_id mapping --- src/lpi/models.py | 2 +- src/lpi/routers/signals.py | 81 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/lpi/models.py b/src/lpi/models.py index 176c6fa..f3b9ef0 100644 --- a/src/lpi/models.py +++ b/src/lpi/models.py @@ -165,7 +165,7 @@ class SignalCreate(BaseModel): # 2. Callers who don't care about provenance don't have to send it. # 3. The GitHub ingestion script can explicitly set 'github_api'. source: str = "api" - + goal_id: str | None = None class Signal(SignalCreate): """Full signal object — inherits SignalCreate fields + server-assigned ones. diff --git a/src/lpi/routers/signals.py b/src/lpi/routers/signals.py index 590466a..ef786fb 100644 --- a/src/lpi/routers/signals.py +++ b/src/lpi/routers/signals.py @@ -58,6 +58,7 @@ """ import uuid +import httpx from datetime import UTC, datetime from fastapi import APIRouter, Depends, HTTPException, Query, status @@ -293,3 +294,83 @@ def get_signal( ) return signal +# ── Phase 4: Dynamic GitHub Integration (Aditi) ────────────────────────────── + +@router.post( + "/sync-github/{goal_id}", + status_code=status.HTTP_200_OK, + summary="Dynamically sync GitHub events to a goal", + description="Polls the public GitHub REST API for a specific repo and ingests recent commits/PRs linked to a goal." +) +async def sync_github_events( + goal_id: str, + repo_name: str = Query(..., description="Target GitHub Repo (e.g. facebook/react or langchain-ai/langchain)"), + user_id: str = Depends(get_current_user), +): + """ + Fetch live events from GitHub and ingest them as signals linked to a goal. + """ + url = f"https://api.github.com/repos/{repo_name}/events" + + # 1. Fetch live data from GitHub + async with httpx.AsyncClient() as client: + response = await client.get(url) + + if response.status_code != 200: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Failed to fetch events from GitHub for repo: {repo_name}. Check if the repo is public and spelled correctly." + ) + + raw_events = response.json() + ingested_count = 0 + + # 2. Parse and Filter High-Value Events (Limit to 20 to protect LLM context window) + for event in raw_events[:20]: + event_type = event.get("type") + + # We only care about code changes and PRs for SMILE phase progression + if event_type in ["PushEvent", "PullRequestEvent"]: + + # Build the creation schema, now including the goal_id + signal_create = SignalCreate( + stream="github", + event_type=event_type, + source=repo_name, + payload=event, + goal_id=goal_id # Linking the signal to the specific goal! + ) + + # Build the full Signal object (mirroring the logic in ingest_signal) + now = datetime.now(UTC) + new_signal = Signal( + id=str(uuid.uuid4()), + user_id=user_id, + timestamp=now, + **signal_create.model_dump() + ) + + # 3. Ingest into the Database + store.insert_signal(new_signal) + + # Log the activity + log_user_activity( + user_id=user_id, + action="github_signal_synced", + resource_id=new_signal.id, + metadata={ + "repo": repo_name, + "event_type": event_type, + "goal_id": goal_id + }, + ) + + ingested_count += 1 + + return { + "status": "success", + "fetched_total": len(raw_events), + "ingested_high_value": ingested_count, + "repo": repo_name, + "goal_id": goal_id + } From 81df0d2f5d00f49c99dc310e4c13f032d7e64ab2 Mon Sep 17 00:00:00 2001 From: Aditi Mehta Date: Thu, 25 Jun 2026 16:57:38 +0530 Subject: [PATCH 2/2] style: auto-sort imports to satisfy ruff --- src/lpi/routers/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lpi/routers/signals.py b/src/lpi/routers/signals.py index ef786fb..d599874 100644 --- a/src/lpi/routers/signals.py +++ b/src/lpi/routers/signals.py @@ -58,9 +58,9 @@ """ import uuid -import httpx from datetime import UTC, datetime +import httpx from fastapi import APIRouter, Depends, HTTPException, Query, status from lpi import store