Skip to content
Merged
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
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ dependencies = [
"httpx>=0.27.0",
"python-dotenv>=1.0.0",
"pyjwt>=2.8.0",
# ── LLM / reasoning layer (Phase 4 — Core Algorithm) ────────────────────
# langgraph : state-machine graph used by src/lpi/langgraph_agent.py
# anthropic : paid Claude API (commented-out code path in _call_llm,
# re-enabled once we have an ANTHROPIC_API_KEY)
# groq : free-tier LLM API — active default until then
"langgraph>=0.2.0",
"anthropic>=0.39.0",
"groq>=0.11.0",
]

[project.optional-dependencies]
Expand Down
7 changes: 4 additions & 3 deletions scripts/ingest_github_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@

# ── GitHub event → LPI signal mapping ────────────────────────────────────────


def map_github_event(event: dict) -> dict | None:
"""Convert a raw GitHub event dict to an LPI SignalCreate payload.

Expand Down Expand Up @@ -227,6 +228,7 @@ def map_github_event(event: dict) -> dict | None:

# ── HTTP helpers ──────────────────────────────────────────────────────────────


def fetch_github_events() -> list[dict]:
"""Call the GitHub Events API and return the raw events list.

Expand Down Expand Up @@ -276,9 +278,7 @@ def post_signal(signal_payload: dict) -> bool:
response = requests.post(url, json=signal_payload, timeout=5)
if response.status_code in (200, 201):
return True
print(
f" [lpi] WARN: POST returned {response.status_code}: {response.text[:100]}"
)
print(f" [lpi] WARN: POST returned {response.status_code}: {response.text[:100]}")
return False
except requests.exceptions.ConnectionError:
print(f" [lpi] ERROR: Cannot connect to {LPI_API_BASE}.")
Expand All @@ -291,6 +291,7 @@ def post_signal(signal_payload: dict) -> bool:

# ── Main ──────────────────────────────────────────────────────────────────────


def main() -> None:
print("=" * 60)
print("LPI GitHub Events Ingestion Script")
Expand Down
112 changes: 112 additions & 0 deletions scripts/test_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import random
import uuid

import requests

# --- CONFIGURATION REQUIRED ---
# Point this to the local ingest endpoint (e.g., "http://localhost:8000/api/v1/signals/")
API_INGEST_URL = "http://localhost:8001/api/v1/signals/"

# Enter the required JWT or dummy token to pass the auth middleware
AUTH_TOKEN = "eyJhbGciOiJFUzI1NiIsImtpZCI6ImI4MTI2OWYxLTIxZDgtNGYyZS1iNzE5LWMyMjQwYTg0MGQ5MCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjU0MzIxL2F1dGgvdjEiLCJzdWIiOiI5MzY0ZjRiMS00NDc4LTQ4MjAtYjMwOC0wOGY5YmM4YWRhZTAiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzgxNjY2NzU3LCJpYXQiOjE3ODE2NjMxNTcsImVtYWlsIjoidGVzdEB0ZXN0LmNvbSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwiLCJwcm92aWRlcnMiOlsiZW1haWwiXX0sInVzZXJfbWV0YWRhdGEiOnsiZW1haWxfdmVyaWZpZWQiOnRydWV9LCJyb2xlIjoiYXV0aGVudGljYXRlZCIsImFhbCI6ImFhbDEiLCJhbXIiOlt7Im1ldGhvZCI6InBhc3N3b3JkIiwidGltZXN0YW1wIjoxNzgxNjYzMTU3fV0sInNlc3Npb25faWQiOiIzMmFjYWY1Zi04OGMyLTRhMzEtOWRkNi1lZTg3YjNlMWY1Y2IiLCJpc19hbm9ueW1vdXMiOmZhbHNlfQ.c9hH6QP0_VBKiuZA7SEn4lMQikWSFYQQmN1fD3ad3GkzhcP9zcXZX45qdW7MFZ7fPAdlYkgG1k6lVnygO3lyTw"

HEADERS = {"Content-Type": "application/json", "Authorization": f"Bearer {AUTH_TOKEN}"}

SMILE_PHASES = [
"reality-emulation",
"concurrent-engineering",
"collective-intelligence",
"contextual-intelligence",
"continuous-intelligence",
"perpetual-wisdom",
]


def generate_and_post_events():
if API_INGEST_URL == "<ENTER_API_URL_HERE>":
print("❌ ERROR: Please update API_INGEST_URL at the top of the script before running.")
return

print(f"🔄 Targeting Activity Signals Ingest API at: {API_INGEST_URL}")

event_types = [
"goal_created",
"goal_updated",
"smile_phase_changed",
"priority_updated",
"goal_completed",
]

print("🚀 Generating 20 structural platform telemetry signals to test the API...")

success_count = 0
fail_count = 0

for i in range(20):
event = random.choice(event_types)

# Mocking a baseline goal context payload
payload = {"goal_id": str(uuid.uuid4()), "title": f"Mock Integration Goal {i + 1}"}

if event == "goal_created":
payload.update(
{"initial_priority": random.randint(4, 8), "initial_phase": "reality-emulation"}
)
elif event == "goal_updated":
payload.update(
{"updated_property": "description", "delta_length": random.randint(15, 120)}
)
elif event == "smile_phase_changed":
current_phase_idx = random.randint(0, 4)
payload.update(
{
"old_phase": SMILE_PHASES[current_phase_idx],
"new_phase": SMILE_PHASES[current_phase_idx + 1],
}
)
elif event == "priority_updated":
payload.update(
{
"previous_priority": random.randint(1, 5),
"target_priority": random.randint(6, 10),
}
)
elif event == "goal_completed":
payload.update(
{
"terminal_phase": "perpetual-wisdom",
"achievement_metric": "completed_ahead_of_schedule",
}
)

# Stripped of id, user_id, and timestamp as the server now handles them securely
signal_entry = {
"stream": "goal_registry",
"event_type": event,
"payload": payload,
"source": "simulated",
}

try:
response = requests.post(API_INGEST_URL, json=signal_entry, headers=HEADERS)

if response.status_code in [200, 201]:
print(f"✅ [SUCCESS] Sent '{event}' - API Responded: {response.status_code}")
success_count += 1
else:
print(
f"❌ [FAILED] Sent '{event}' - API Responded: {response.status_code} - {response.text}"
)
fail_count += 1

except Exception as e:
print(f"⚠️ [CONNECTION ERROR] Could not reach API: {str(e)}")
break

print(
f"\n🏁 API Testing Complete! Successful POSTs: {success_count} | Failed POSTs: {fail_count}"
)


if __name__ == "__main__":
generate_and_post_events()
11 changes: 9 additions & 2 deletions src/lpi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ class Settings(BaseSettings):
supabase_key: str = ""
supabase_service_role_key: str = ""
supabase_jwt_secret: str = ""
llm_provider: str = "anthropic"
llm_model: str = "claude-sonnet-4-20250514"
# ── LLM provider selection ────────────────────────────────────────────────
# llm_provider: "groq" (default, free tier) or "anthropic" (when we have
# a paid Claude API key). Switch by setting LLM_PROVIDER in .env.
# See src/lpi/langgraph_agent.py for how the provider is selected.
llm_provider: str = "groq"
llm_model: str = "llama-3.3-70b-versatile"
anthropic_api_key: str = ""
groq_api_key: str = ""
daily_cost_cap_usd: float = 10.0
github_client_id: str = ""
github_client_secret: str = ""
Expand All @@ -26,6 +31,8 @@ def admin_ids_list(self) -> list[str]:
"supabase_key",
"supabase_service_role_key",
"supabase_jwt_secret",
"anthropic_api_key",
"groq_api_key",
mode="before",
)
@classmethod
Expand Down
Loading
Loading