Skip to content
Open
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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,24 @@ The API server provides:

For more details, see the [API README](./api/README.md).

### Publishing to understand-quickly (opt-in)

DeepWiki can emit its generated wiki as a [`generic@1`](https://github.com/looptech-ai/understand-quickly/blob/main/schemas/generic@1.json) knowledge graph and (optionally) register it with [`looptech-ai/understand-quickly`](https://github.com/looptech-ai/understand-quickly), a public registry of code-knowledge graphs that ships an MCP server and a stable `registry.json` API.

```bash
# Existing markdown / json export — unchanged.
curl -X POST http://localhost:8001/export/wiki \
-H "content-type: application/json" \
-d '{"repo_url":"https://github.com/owner/repo","format":"json","pages":[...]}' > wiki.json

# New: emit the knowledge graph and (optionally) ping the registry.
curl -X POST http://localhost:8001/export/wiki \
-H "content-type: application/json" \
-d '{"repo_url":"https://github.com/owner/repo","format":"graph","publish":true,"pages":[...]}' > graph.json
```

Set `UNDERSTAND_QUICKLY_TOKEN` in the API server env (a fine-grained PAT with `Repository dispatches: write` on `looptech-ai/understand-quickly` only) to enable the dispatch step. With the token unset, `format=graph` still emits the file — the dispatch is simply skipped. See the [integration protocol](https://github.com/looptech-ai/understand-quickly/blob/main/docs/integrations/protocol.md) for the full contract.

## 🔌 OpenRouter Integration

DeepWiki now supports [OpenRouter](https://openrouter.ai/) as a model provider, giving you access to hundreds of AI models through a single API:
Expand Down
94 changes: 89 additions & 5 deletions api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,42 @@ class WikiExportRequest(BaseModel):
"""
repo_url: str = Field(..., description="URL of the repository")
pages: List[WikiPage] = Field(..., description="List of wiki pages to export")
format: Literal["markdown", "json"] = Field(..., description="Export format (markdown or json)")
format: Literal["markdown", "json", "graph"] = Field(
...,
description=(
"Export format. 'markdown' / 'json' are the existing wiki dumps; "
"'graph' emits a generic@1 knowledge graph for the "
"looptech-ai/understand-quickly registry."
),
)
publish: bool = Field(
False,
description=(
"If true, after producing the export also fire a "
"repository_dispatch event at looptech-ai/understand-quickly "
"so the registry resyncs the entry. Opt-in; requires "
"UNDERSTAND_QUICKLY_TOKEN in the server env. No-ops cleanly "
"if the token is missing."
),
)
repo: Optional[str] = Field(
None,
description=(
"Optional 'owner/repo' override for the registry id. If "
"omitted, derived from `repo_url`. Must match the owner/repo "
"implied by `repo_url` when set — mismatches are rejected to "
"prevent dispatching syncs for unrelated entries."
),
)
commit: Optional[str] = Field(
None,
description=(
"Optional 40-hex git commit SHA to embed in the graph "
"metadata. If omitted, the server attempts to resolve HEAD "
"from its local checkout (best-effort) and otherwise leaves "
"`metadata.commit` unset."
),
)
Comment on lines +136 to +153

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To fully support the commit metadata field in the generated graph (as mentioned in the PR description and implemented in build_graph_payload), consider adding a commit field to the WikiExportRequest model. This allows the caller (e.g., a CI workflow or an orchestrator) to provide the specific git SHA associated with the wiki content.

    repo: Optional[str] = Field(
        None,
        description=(
            "Optional 'owner/repo' override for the registry id. If "
            "omitted, derived from `repo_url`."
        ),
    )
    commit: Optional[str] = Field(
        None,
        description="Optional 40-hex git commit SHA to embed in the graph metadata.",
    )


# --- Model Configuration Models ---
class Model(BaseModel):
Expand Down Expand Up @@ -227,7 +262,7 @@ async def get_model_config():
@app.post("/export/wiki")
async def export_wiki(request: WikiExportRequest):
"""
Export wiki content as Markdown or JSON.
Export wiki content as Markdown, JSON, or a knowledge graph.

Args:
request: The export request containing wiki pages and format
Expand All @@ -245,24 +280,73 @@ async def export_wiki(request: WikiExportRequest):
# Get current timestamp for the filename
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

publish_status: Optional[Dict[str, Any]] = None
headers: Dict[str, str] = {}

if request.format == "markdown":
# Generate Markdown content
content = generate_markdown_export(request.repo_url, request.pages)
filename = f"{repo_name}_wiki_{timestamp}.md"
media_type = "text/markdown"
elif request.format == "graph":
# generic@1 knowledge graph for looptech-ai/understand-quickly.
from api.publish import (
build_graph_payload,
derive_owner_repo,
git_head_sha,
publish as publish_to_registry,
)
Comment on lines +293 to +298

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If you apply the suggestion to include the commit SHA, ensure you also import the git_head_sha helper here.

Suggested change
from api.publish import (
build_graph_payload,
derive_owner_repo,
publish as publish_to_registry,
)
from api.publish import (
build_graph_payload,
derive_owner_repo,
git_head_sha,
publish as publish_to_registry,
)


derived_owner_repo = derive_owner_repo(request.repo_url)
commit = request.commit or git_head_sha()

payload = build_graph_payload(
[page.model_dump() for page in request.pages],
repo_url=request.repo_url,
commit=commit,
)
Comment on lines +303 to +307

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Pass the commit from the request to the build_graph_payload function so it can be included in the graph metadata.

Suggested change
payload = build_graph_payload(
[page.model_dump() for page in request.pages],
repo_url=request.repo_url,
)
payload = build_graph_payload(
[page.model_dump() for page in request.pages],
repo_url=request.repo_url,
commit=request.commit,
)

Comment on lines +303 to +307

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The PR description mentions that metadata.commit is embedded when available, and api/publish.py includes a git_head_sha helper for this purpose. However, build_graph_payload is currently called without the commit argument, and git_head_sha is never invoked. To fulfill the intended functionality, you should attempt to resolve the current commit SHA.

Suggested change
payload = build_graph_payload(
[page.model_dump() for page in request.pages],
repo_url=request.repo_url,
)
payload = build_graph_payload(
[page.model_dump() for page in request.pages],
repo_url=request.repo_url,
commit=git_head_sha(),
)

content = json.dumps(payload, indent=2)
filename = f"{repo_name}_graph_{timestamp}.json"
media_type = "application/json"

if request.publish:
# Reject explicit `repo` overrides that don't match the
# derived owner/repo from `repo_url`. Without this an
# unauthenticated client could trigger a sync-entry
# dispatch for any registry id once the server is
# configured with a token.
if request.repo and derived_owner_repo and request.repo != derived_owner_repo:
raise HTTPException(
status_code=400,
detail=(
f"`repo` override ({request.repo!r}) does not "
f"match owner/repo derived from `repo_url` "
f"({derived_owner_repo!r})."
),
)
owner_repo = request.repo or derived_owner_repo
publish_status = await asyncio.to_thread(
publish_to_registry, payload, owner_repo=owner_repo
)
headers["X-Understand-Quickly-Dispatched"] = (
"true" if publish_status.get("dispatched") else "false"
)
if publish_status.get("reason"):
headers["X-Understand-Quickly-Reason"] = str(
publish_status["reason"]
)
Comment on lines +312 to +337
else: # JSON format
# Generate JSON content
content = generate_json_export(request.repo_url, request.pages)
filename = f"{repo_name}_wiki_{timestamp}.json"
media_type = "application/json"

# Create response with appropriate headers for file download
headers["Content-Disposition"] = f"attachment; filename={filename}"
response = Response(
content=content,
media_type=media_type,
headers={
"Content-Disposition": f"attachment; filename={filename}"
}
headers=headers,
)

return response
Expand Down
Loading