Add Open Library Omi integration app#7415
Conversation
Greptile SummaryThis PR adds a new standalone Omi plugin app (
Confidence Score: 4/5Safe to merge; the app is a new, self-contained plugin with no changes to existing code paths. The two findings are quality/consistency gaps in a brand-new standalone plugin. The missing 404 handling in search_subject exposes a raw HTTP error string to users when an unrecognised subject slug is queried, and the absent schema constraints in two tool manifests mean an LLM has no upfront signal that parameters are required. Neither issue affects existing functionality or other parts of the codebase. plugins/omi-open-library-app/main.py — exception handling consistency in search_subject and manifest schema completeness. Important Files Changed
Sequence DiagramsequenceDiagram
participant Omi as Omi Client
participant App as FastAPI App
participant OL as Open Library
Omi->>App: GET /.well-known/omi-tools.json
App-->>Omi: Tool manifest with 3 tools
Omi->>App: POST /tools/search_books
App->>OL: GET /search.json with query params
OL-->>App: docs array
App-->>Omi: ChatToolResponse with formatted results
Omi->>App: POST /tools/get_book_details
alt work_id supplied
App->>OL: GET /works/OLxxxxW.json
else isbn supplied
App->>OL: GET /api/books with bibkeys param
end
OL-->>App: work or edition metadata
App-->>Omi: ChatToolResponse with formatted details
Omi->>App: POST /tools/search_subject
App->>OL: GET /subjects/slug.json
OL-->>App: works array
App-->>Omi: ChatToolResponse with subject book list
Reviews (1): Last reviewed commit: "Add Open Library Omi integration app" | Re-trigger Greptile |
| try: | ||
| data = await _request_json(f"/subjects/{slug}.json", params={"limit": limit}) | ||
| works = data.get("works", [])[:limit] | ||
| if not works: | ||
| return ChatToolResponse(result=f"No books found for subject {subject}.") | ||
|
|
||
| title = _clean_text(data.get("name")) or subject | ||
| lines = [_format_subject_work(work, i + 1) for i, work in enumerate(works)] | ||
| return ChatToolResponse(result=f"Open Library books for subject {title}:\n\n" + "\n\n".join(lines)) | ||
| except (httpx.HTTPError, ValueError) as exc: | ||
| return ChatToolResponse(error=f"Open Library subject search failed: {exc}") |
There was a problem hiding this comment.
search_subject doesn't handle 404 responses with a friendly message. Open Library's /subjects/{slug}.json endpoint returns HTTP 404 for unrecognised subject slugs. _request_json calls raise_for_status(), which raises httpx.HTTPStatusError — a subclass of httpx.HTTPError, so it falls into the generic catch block and surfaces as "Open Library subject search failed: Client error '404 Not Found'…" instead of a clean "no books found" reply. get_book_details already handles this correctly with a dedicated except httpx.HTTPStatusError clause.
| try: | |
| data = await _request_json(f"/subjects/{slug}.json", params={"limit": limit}) | |
| works = data.get("works", [])[:limit] | |
| if not works: | |
| return ChatToolResponse(result=f"No books found for subject {subject}.") | |
| title = _clean_text(data.get("name")) or subject | |
| lines = [_format_subject_work(work, i + 1) for i, work in enumerate(works)] | |
| return ChatToolResponse(result=f"Open Library books for subject {title}:\n\n" + "\n\n".join(lines)) | |
| except (httpx.HTTPError, ValueError) as exc: | |
| return ChatToolResponse(error=f"Open Library subject search failed: {exc}") | |
| try: | |
| data = await _request_json(f"/subjects/{slug}.json", params={"limit": limit}) | |
| works = data.get("works", [])[:limit] | |
| if not works: | |
| return ChatToolResponse(result=f"No books found for subject {subject}.") | |
| title = _clean_text(data.get("name")) or subject | |
| lines = [_format_subject_work(work, i + 1) for i, work in enumerate(works)] | |
| return ChatToolResponse(result=f"Open Library books for subject {title}:\n\n" + "\n\n".join(lines)) | |
| except httpx.HTTPStatusError as exc: | |
| if exc.response.status_code == 404: | |
| return ChatToolResponse(result=f"No books found for subject {subject}.") | |
| return ChatToolResponse(error=f"Open Library subject search failed: {exc}") | |
| except (httpx.HTTPError, ValueError) as exc: | |
| return ChatToolResponse(error=f"Open Library subject search failed: {exc}") |
| "parameters": { | ||
| "type": "object", | ||
| "properties": { | ||
| "query": { | ||
| "type": "string", | ||
| "description": "Free-form book search query, such as a title, author, topic, or ISBN.", | ||
| }, | ||
| "author": { | ||
| "type": "string", | ||
| "description": "Optional author name filter.", | ||
| }, | ||
| "subject": { | ||
| "type": "string", | ||
| "description": "Optional subject filter, such as science fiction or product management.", | ||
| }, | ||
| "limit": { | ||
| "type": "integer", | ||
| "description": "Maximum books to return. Defaults to 5, maximum 10.", | ||
| }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
The
search_books manifest schema has no required field, but the endpoint will reject a call with no query, author, or subject. An LLM relying solely on the schema has no way to know this constraint and will receive a runtime error response instead of schema-level guidance. Adding a description hint or anyOf constraint lets the Omi platform surface the requirement before the call is made. The same gap exists in get_book_details — neither work_id nor isbn is marked required, yet at least one must be present.
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "query": { | |
| "type": "string", | |
| "description": "Free-form book search query, such as a title, author, topic, or ISBN.", | |
| }, | |
| "author": { | |
| "type": "string", | |
| "description": "Optional author name filter.", | |
| }, | |
| "subject": { | |
| "type": "string", | |
| "description": "Optional subject filter, such as science fiction or product management.", | |
| }, | |
| "limit": { | |
| "type": "integer", | |
| "description": "Maximum books to return. Defaults to 5, maximum 10.", | |
| }, | |
| }, | |
| }, | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "query": { | |
| "type": "string", | |
| "description": "Free-form book search query, such as a title, author, topic, or ISBN.", | |
| }, | |
| "author": { | |
| "type": "string", | |
| "description": "Optional author name filter.", | |
| }, | |
| "subject": { | |
| "type": "string", | |
| "description": "Optional subject filter, such as science fiction or product management.", | |
| }, | |
| "limit": { | |
| "type": "integer", | |
| "description": "Maximum books to return. Defaults to 5, maximum 10.", | |
| }, | |
| }, | |
| "description": "At least one of query, author, or subject must be provided.", | |
| }, |
|
Addressed the Greptile quality feedback in commit 0348468:
Validation rerun with the app pinned Python 3.11 deps:
|
kodjima33
left a comment
There was a problem hiding this comment.
thanks for adding the Open Library app
Summary
plugins/omi-open-library-appValidation
plugins/omi-open-library-app/.venv/bin/python -m py_compile plugins/omi-open-library-app/main.pytype: objectschemassearch_books,get_book_details, andsearch_subjectgit diff --cached --checkCandidate integration app for #3120.