馃Ч chore: Py example cassette - #331
Conversation
40c65e1 to
2090658
Compare
Signed-off-by: John McBride <john@papercompute.com>
2090658 to
bb31b8a
Compare
|
| Filename | Overview |
|---|---|
| pkg/cassette/examples/py-example/main.py | Implements the cassette service and contracts correctly overall, but malformed successful upstream payloads can escape as internal 500 responses. |
| pkg/cassette/examples/py-example/docker-compose.yaml | Connects PostgreSQL, Tapes, and the cassette with consistent service URLs and retry-based cassette discovery. |
| pkg/cassette/examples/py-example/cassette.toml | Declares deployment metadata equivalent to the manifest embedded in the service OpenAPI document. |
| pkg/cassette/examples/py-example/Dockerfile | Builds the locked Python environment and runs the cassette as an unprivileged user. |
| pkg/cassette/examples/py-example/tests/test_main.py | Covers the primary contract and input validation paths but does not exercise malformed upstream responses. |
Sequence Diagram
sequenceDiagram
participant Client
participant Tapes as Tapes API / MCP
participant Cassette as Prompt cassette
participant TraceAPI as Tapes trace API
Client->>Tapes: Call prompt.get_prompt
Tapes->>Cassette: POST /api/prompt/get
Cassette->>TraceAPI: "GET /v1/traces?session_id=..."
TraceAPI-->>Cassette: Ordered trace summaries
Cassette-->>Tapes: First non-empty user prompt
Tapes-->>Client: MCP structured result
Prompt To Fix All With AI
### Issue 1
pkg/cassette/examples/py-example/main.py:71-80
**Validate upstream response shapes**
A JSON-decodable 200 response such as `null`, `{"items": null}`, or an array containing a non-object raises an uncaught `AttributeError` or `TypeError`, returning an internal 500 instead of the documented 502 for an invalid Tapes response.
```suggestion
try:
response.raise_for_status()
payload = response.json()
if not isinstance(payload, dict):
raise ValueError("response root is not an object")
items = payload.get("items", [])
if not isinstance(items, list):
raise ValueError("response items is not an array")
for item in items:
if not isinstance(item, dict):
raise ValueError("response item is not an object")
prompt = item.get("user_prompt", "")
if isinstance(prompt, str) and prompt:
return {"session_id": session_id, "prompt": prompt}
except (httpx.HTTPError, ValueError) as error:
raise HTTPException(status_code=502, detail="invalid response from tapes") from error
raise HTTPException(status_code=404, detail="prompt not found")
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "馃Ч chore: Py example cassette" | Re-trigger Greptile
| try: | ||
| response.raise_for_status() | ||
| items = response.json().get("items", []) | ||
| except (httpx.HTTPError, ValueError) as error: | ||
| raise HTTPException(status_code=502, detail="invalid response from tapes") from error | ||
| for item in items: | ||
| prompt = item.get("user_prompt", "") | ||
| if isinstance(prompt, str) and prompt: | ||
| return {"session_id": session_id, "prompt": prompt} | ||
| raise HTTPException(status_code=404, detail="prompt not found") |
There was a problem hiding this comment.
Validate upstream response shapes
A JSON-decodable 200 response such as null, {"items": null}, or an array containing a non-object raises an uncaught AttributeError or TypeError, returning an internal 500 instead of the documented 502 for an invalid Tapes response.
| try: | |
| response.raise_for_status() | |
| items = response.json().get("items", []) | |
| except (httpx.HTTPError, ValueError) as error: | |
| raise HTTPException(status_code=502, detail="invalid response from tapes") from error | |
| for item in items: | |
| prompt = item.get("user_prompt", "") | |
| if isinstance(prompt, str) and prompt: | |
| return {"session_id": session_id, "prompt": prompt} | |
| raise HTTPException(status_code=404, detail="prompt not found") | |
| try: | |
| response.raise_for_status() | |
| payload = response.json() | |
| if not isinstance(payload, dict): | |
| raise ValueError("response root is not an object") | |
| items = payload.get("items", []) | |
| if not isinstance(items, list): | |
| raise ValueError("response items is not an array") | |
| for item in items: | |
| if not isinstance(item, dict): | |
| raise ValueError("response item is not an object") | |
| prompt = item.get("user_prompt", "") | |
| if isinstance(prompt, str) and prompt: | |
| return {"session_id": session_id, "prompt": prompt} | |
| except (httpx.HTTPError, ValueError) as error: | |
| raise HTTPException(status_code=502, detail="invalid response from tapes") from error | |
| raise HTTPException(status_code=404, detail="prompt not found") |
Knowledge Base Used: Session and trace API
Prompt To Fix With AI
This is a comment left during a code review.
Path: pkg/cassette/examples/py-example/main.py
Line: 71-80
Comment:
**Validate upstream response shapes**
A JSON-decodable 200 response such as `null`, `{"items": null}`, or an array containing a non-object raises an uncaught `AttributeError` or `TypeError`, returning an internal 500 instead of the documented 502 for an invalid Tapes response.
```suggestion
try:
response.raise_for_status()
payload = response.json()
if not isinstance(payload, dict):
raise ValueError("response root is not an object")
items = payload.get("items", [])
if not isinstance(items, list):
raise ValueError("response items is not an array")
for item in items:
if not isinstance(item, dict):
raise ValueError("response item is not an object")
prompt = item.get("user_prompt", "")
if isinstance(prompt, str) and prompt:
return {"session_id": session_id, "prompt": prompt}
except (httpx.HTTPError, ValueError) as error:
raise HTTPException(status_code=502, detail="invalid response from tapes") from error
raise HTTPException(status_code=404, detail="prompt not found")
```
**Knowledge Base Used:** [Session and trace API](https://app.greptile.com/paper-compute/-/custom-context/knowledge-base/papercomputeco/tapes/-/docs/session-and-trace-api.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Python tapes cassette example. Simple cassette, for some blog content.
Refs REL-118