This repository has no description
1# Recommendation Engine — HTTP API
2
3Standalone FastAPI service for Tangled repo/issue discovery.
4
5**Storage:** `DATA_STORAGE=sql` (default, Postgres+pgvector) or `DATA_STORAGE=git`
6(in-memory numpy+jsonl bundle cloned from `REC_DATA_GIT_URL` at boot). See
7`.env.example`.
8
9Endpoints: `/recommendations`, `/questionnaire` (sql only today), `/health`.
10
11Base URL: whatever you deploy to (the Tangled appview points `TANGLED_DISCOVER_ENDPOINT`
12at the `/recommendations` path).
13
14---
15
16## `GET /recommendations`
17
18The contract consumed by the Tangled appview. Returns the user's interest chips plus
19ranked repo + issue recommendations, with the user's own/collaborated repos and
20self-authored issues excluded.
21
22**Query params**
23
24| Param | Required | Notes |
25| --- | --- | --- |
26| `handle` | yes | The user's Tangled DID, e.g. `did:plc:abc123`. |
27| `gh` | no | Connected GitHub username. Accepted but currently ignored (no GitHub data). |
28
29**Response** `200 OK` — see [`schema.md`](../../schema.md) for the authoritative shape. Summary:
30
31```jsonc
32{
33 "profile": {
34 "interests": [{ "label": "nix", "slug": "nix" }], // from the user's repo topics
35 "languages": [], // no language signal yet
36 "sources": { "tangled": { "repos": 10 } } // github omitted (no data)
37 },
38 "repos": [{
39 "name": "...", "owner": "@handle", "language": "", "description": "...",
40 "stars": 0, "openIssues": 3, "lastActive": "<RFC3339>",
41 "url": "https://tangled.org/@handle/name",
42 "basedOnRepoUrl": "https://tangled.org/@you/your-seed-repo"
43 }],
44 "issues": [{
45 "title": "...", "repo": "handle/name", "owner": "@handle",
46 "issueUri": "at://did:plc:…/sh.tangled.repo.issue/3k…",
47 "repoDid": "did:plc:...", "rkey": "3k...",
48 "url": "https://tangled.org/@handle/name",
49 "basedOnRepoUrl": "https://tangled.org/@you/your-seed-repo",
50 "repoReadme": "...",
51 "labels": [], "comments": 0, "language": "", "lastActive": "<RFC3339>"
52 }]
53}
54```
55
56Notes:
57- Empty user → `"repos": []` (the frontend then shows its cold-start view).
58- `stars`/`comments`/`language`/`languages` are stubbed (no source in the shared DB yet).
59- Issues omit `number` (issue permalink); the frontend resolves it from `(repoDid, rkey)`.
60 `url` is the parent repo; `basedOnRepoUrl` is the user's seed repo that surfaced the hit.
61- `basedOnRepoUrl` on repos is the same seed attribution (the user's repo whose README
62 embedding produced the closest match).
63
64---
65
66## `GET /questionnaire`
67
68Return the cached AI-solve questionnaire JSON for an issue (written by the questionnaire
69Cloud Run job). Does not generate on demand — returns `404` if not cached yet.
70
71**Query params**
72
73| Param | Required | Notes |
74| --- | --- | --- |
75| `issue` | yes* | Full `at://…/sh.tangled.repo.issue/<rkey>` URI, or bare rkey (DB lookup). |
76| `issue-uri` | yes* | Alias for `issue`. |
77
78\* Provide one of `issue` or `issue-uri`.
79
80**Response** `200 OK` — questionnaire object (version 2: `introduction`, `items[]`, …).
81
82**Errors**
83
84| Status | When |
85| --- | --- |
86| `400` | Missing param, invalid URI, or ambiguous rkey |
87| `404` | Issue URI valid but no cached questionnaire |
88
89```bash
90curl 'localhost:8000/questionnaire?issue=at://did:plc:…/sh.tangled.repo.issue/3lv…'
91```
92
93---
94
95## `GET /health`
96
97```jsonc
98{ "status": "ok", "db": true }
99```
100
101`status` is `"degraded"` with `db:false` (and an `error`) if the database is unreachable.
102
103---
104
105## Conventions
106
107- Timestamps (`lastActive`) are RFC-3339; the frontend humanizes them.
108- `owner` carries a leading `@`; repo `url` is absolute.
109- Ordering is the engine's call — arrays are returned already ranked, most relevant first.
110- Errors: any non-200 (or timeout) makes the appview fall back to its cold-start view; no
111 structured error body is required.