-
-
Notifications
You must be signed in to change notification settings - Fork 420
docs: publish Skippy native API reference #1217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
i386
merged 32 commits into
jd/fix-issue-986-reasoning-effort-on-1194
from
agent/skippy-native-api-docs
Aug 10, 2026
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
85dd4c9
Split Skippy by functional boundary
i386 48998f4
Rebuild llama patch queue by functional boundary
i386 e3733fc
Add explicit standard library includes to Skippy modules
i386 0779302
Add explicit tokenization limit dependency
i386 425b11d
Address Skippy queue review findings
i386 354ff23
Advertise Skippy chat feature support
i386 c3a297e
Fix split GGUF stage inventory discovery (#1199)
i386 ce4b27b
feat: pass reasoning effort through Skippy templates
i386 6c76c83
feat: advertise reasoning efforts in model listings
i386 5ff95a5
docs: require Skippy ABI inventories in PRs
i386 f7d7efa
docs: clarify Skippy ABI lockstep policy
i386 e4a5ed2
refactor: remove legacy Skippy chat ABI
i386 7e3d60b
fix: stop advertising reasoning effort values
i386 b498e45
fix: preserve Skippy no-thinking override
i386 f37fa82
docs: publish Skippy native API reference
i386 1f715f2
fix: address Skippy API doc review comments
i386 d75ec05
Split Skippy by functional boundary
i386 47eb1c4
Rebuild llama patch queue by functional boundary
i386 d0a20d6
Add explicit standard library includes to Skippy modules
i386 0927a4f
Add explicit tokenization limit dependency
i386 534a646
Address Skippy queue review findings
i386 82b00e9
Advertise Skippy chat feature support
i386 618e785
Fix split GGUF stage inventory discovery (#1199)
i386 0852cea
feat: pass reasoning effort through Skippy templates
i386 8884319
feat: advertise reasoning efforts in model listings
i386 fab39ff
docs: require Skippy ABI inventories in PRs
i386 eb2b865
docs: clarify Skippy ABI lockstep policy
i386 0fdc27c
refactor: remove legacy Skippy chat ABI
i386 bc64ab2
fix: stop advertising reasoning effort values
i386 38ac03a
fix: preserve Skippy no-thinking override
i386 e090829
Merge remote-tracking branch 'mesh/jd/fix-issue-986-reasoning-effort-…
i386 3c442dd
Merge remote-tracking branch 'mesh/jd/fix-issue-986-reasoning-effort-…
i386 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,261 @@ | ||
| #!/usr/bin/env python3 | ||
| """Generate the website's Skippy native API reference from patched headers.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import re | ||
| import sys | ||
| from dataclasses import dataclass | ||
| from pathlib import Path | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class Function: | ||
| name: str | ||
| declaration: str | ||
| brief: str | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class Header: | ||
| name: str | ||
| brief: str | ||
| declarations: tuple[str, ...] | ||
| functions: tuple[Function, ...] | ||
|
|
||
|
|
||
| def normalize_declaration(declaration: str) -> str: | ||
| return re.sub(r"\s+", " ", declaration.strip()) | ||
|
|
||
|
|
||
| def pretty_declaration(declaration: str) -> str: | ||
| declaration = normalize_declaration(declaration) | ||
| declaration = declaration.replace("(", "(\n ", 1) | ||
| declaration = declaration.replace(", ", ",\n ") | ||
| declaration = re.sub(r"\s+\);$", "\n);", declaration) | ||
| return declaration | ||
|
|
||
|
|
||
| def anchor_id(prefix: str, value: str) -> str: | ||
| slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-") | ||
| return f"skippy-{prefix}-{slug}" | ||
|
|
||
|
|
||
| def header_anchor(header: Header) -> str: | ||
| return anchor_id("header", header.name) | ||
|
|
||
|
|
||
| def function_anchor(function: Function) -> str: | ||
| return anchor_id("fn", function.name) | ||
|
|
||
|
|
||
| def comment_brief(comment: str) -> str: | ||
| lines = [] | ||
| for line in comment.splitlines(): | ||
| line = re.sub(r"^\s*\*/\s*$", "", line) | ||
| line = re.sub(r"^\s*/?\*+\s?", "", line) | ||
| line = re.sub(r"\s*\*/\s*$", "", line) | ||
| lines.append(line.strip()) | ||
| normalized = "\n".join(lines) | ||
| match = re.search(r"@brief\s+(.+?)(?=\n@\w+|$)", normalized, re.DOTALL) | ||
| if match is None: | ||
| return "" | ||
| return re.sub(r"\s+", " ", match.group(1)).strip() | ||
|
|
||
|
|
||
| def parse_header(path: Path) -> Header: | ||
| text = path.read_text() | ||
| file_comment = re.search(r"/\*\*.*?@file.*?\*/", text, re.DOTALL) | ||
| if file_comment is None: | ||
| raise ValueError(f"missing file documentation in {path}") | ||
| brief = comment_brief(file_comment.group(0)) | ||
| if not brief: | ||
| raise ValueError(f"missing @brief for {path}") | ||
|
|
||
| functions: list[Function] = [] | ||
| function_pattern = re.compile(r"LLAMA_API\s+.*?;", re.DOTALL) | ||
| for match in function_pattern.finditer(text): | ||
| declaration = normalize_declaration(match.group(0)) | ||
| name_match = re.search(r"\b(skippy_[a-zA-Z0-9_]+)\s*\(", declaration) | ||
| if name_match is None: | ||
| continue | ||
| preceding = text[: match.start()] | ||
| comments = list(re.finditer(r"/\*\*.*?\*/", preceding, re.DOTALL)) | ||
| comment = comments[-1] if comments and not preceding[comments[-1].end():].strip() else None | ||
| function_brief = comment_brief(comment.group(0)) if comment else "" | ||
| if not function_brief: | ||
| raise ValueError(f"missing @brief for {name_match.group(1)} in {path}") | ||
|
i386 marked this conversation as resolved.
|
||
| functions.append(Function(name_match.group(1), declaration, function_brief)) | ||
|
|
||
| declarations: list[str] = [] | ||
| for match in re.finditer(r"^(?:struct|enum)\s+(skippy_[a-zA-Z0-9_]+)\s*(?:\{|;)", text, re.MULTILINE): | ||
| declarations.append(match.group(1)) | ||
| for match in re.finditer(r"^#define\s+(SKIPPY_[A-Z0-9_]+)(?:[ \t]+(.+))?$", text, re.MULTILINE): | ||
| if match.group(1) == "SKIPPY_H" or match.group(1).endswith("_H"): | ||
| continue | ||
| value = (match.group(2) or "").strip() | ||
| declarations.append(f"{match.group(1)} = {value}" if value else match.group(1)) | ||
|
i386 marked this conversation as resolved.
|
||
|
|
||
| return Header(path.name, brief, tuple(dict.fromkeys(declarations)), tuple(functions)) | ||
|
|
||
|
|
||
| def render(headers: list[Header], include_dir: Path) -> str: | ||
| functions = [function for header in headers for function in header.functions] | ||
| lines = [ | ||
| "---", | ||
| "title: Skippy native API", | ||
| "description: Generated reference for the capability-oriented Skippy C ABI.", | ||
| "---", | ||
| "", | ||
| "<!-- This file is generated by scripts/generate-skippy-api-doc.py. Do not edit it by hand. -->", | ||
| "", | ||
| "# Skippy native API", | ||
| "", | ||
| "This reference is generated from the patched llama.cpp public headers. It documents the native C ABI used by Skippy's Rust FFI layer and staged runtime. The ABI is experimental and versioned for lockstep native/Rust builds.", | ||
| "", | ||
| f"Current generated surface: **{len(headers)} headers** and **{len(functions)} exported functions**.", | ||
| "", | ||
| "## Quick navigation", | ||
| "", | ||
| '<nav class="skippy-api-quicknav" aria-label="Skippy API quick navigation" data-pagefind-ignore="all">', | ||
| ' <a class="skippy-api-quicknav__item" href="#skippy-include-map"><span class="skippy-api-quicknav__number">01</span><strong>Headers</strong><small>Choose a capability</small></a>', | ||
| ' <a class="skippy-api-quicknav__item" href="#skippy-function-index"><span class="skippy-api-quicknav__number">02</span><strong>Functions</strong><small>Jump to a symbol</small></a>', | ||
| ' <a class="skippy-api-quicknav__item" href="#skippy-abi-conventions"><span class="skippy-api-quicknav__number">03</span><strong>ABI rules</strong><small>Ownership and buffers</small></a>', | ||
| ' <a class="skippy-api-quicknav__item" href="#skippy-native-declarations"><span class="skippy-api-quicknav__number">04</span><strong>Types</strong><small>Enums and structs</small></a>', | ||
| "</nav>", | ||
| "", | ||
| '<div id="skippy-function-index" class="skippy-api-index" data-pagefind-ignore="all">', | ||
| ' <div class="skippy-api-index__heading">', | ||
| ' <p class="skippy-api-index__eyebrow">Symbol index</p>', | ||
| ' <p class="skippy-api-index__hint">Select a function to jump directly to its native declaration.</p>', | ||
| " </div>", | ||
| ' <div class="skippy-api-index__groups">', | ||
| ] | ||
| for header in headers: | ||
| if not header.functions: | ||
| continue | ||
| lines.extend( | ||
| [ | ||
| ' <section class="skippy-api-index__group">', | ||
| f' <a class="skippy-api-index__group-title" href="#{header_anchor(header)}"><code>{header.name}</code><span>{len(header.functions)} functions</span></a>', | ||
| ' <div class="skippy-api-index__functions">', | ||
| ] | ||
| ) | ||
| for function in header.functions: | ||
| lines.append(f' <a href="#{function_anchor(function)}"><code>{function.name}</code></a>') | ||
| lines.extend([" </div>", " </section>"]) | ||
| lines.extend( | ||
| [ | ||
| " </div>", | ||
| "</div>", | ||
| "", | ||
| '<a id="skippy-include-map"></a>', | ||
| "## Include map", | ||
| "", | ||
| "Include the umbrella header for the complete surface:", | ||
| "", | ||
| "```cpp", | ||
| "#include <skippy.h>", | ||
| "```", | ||
| "", | ||
| "Capability consumers can include a narrower header:", | ||
| "", | ||
| "| Header | Used for |", | ||
| "|---|---|", | ||
| ] | ||
| ) | ||
| for header in headers: | ||
| header_path = "include/skippy.h" if header.name == "skippy.h" else f"include/skippy/{header.name}" | ||
| lines.append(f"| `{header_path}` | {header.brief} |") | ||
|
|
||
| lines.extend( | ||
| [ | ||
| "", | ||
| '<a id="skippy-abi-conventions"></a>', | ||
| "## ABI conventions", | ||
| "", | ||
| "- All exported functions use the `skippy_` prefix and return `skippy_status`, except direct discovery values and `void` cleanup functions.", | ||
| "- Handles are opaque C structs. Successful creators transfer ownership to the caller; the matching `*_free` function releases them.", | ||
| "- Buffer-writing functions use caller-provided pointers and capacities, returning the required size through an output parameter and `SKIPPY_STATUS_BUFFER_TOO_SMALL` when needed.", | ||
| "- Errors returned through `struct skippy_error ** out_error` are owned by the caller and must be released with `skippy_error_free`.", | ||
| "- Check `skippy_abi_version()` and `skippy_abi_features()` before using optional capabilities.", | ||
| "", | ||
| '<a id="skippy-exported-functions"></a>', | ||
| "## Exported functions", | ||
| "", | ||
| ] | ||
| ) | ||
| for header in headers: | ||
| if not header.functions: | ||
| continue | ||
| lines.extend([f'<a id="{header_anchor(header)}"></a>', f"### `{header.name}`", ""]) | ||
| for function in header.functions: | ||
| lines.extend( | ||
| [ | ||
| f'<a id="{function_anchor(function)}"></a>', | ||
| f"#### `{function.name}`", | ||
| "", | ||
| function.brief, | ||
| "", | ||
| "```cpp", | ||
| pretty_declaration(function.declaration), | ||
| "```", | ||
| "", | ||
| ] | ||
| ) | ||
| lines.extend(['<a class="skippy-api-backlink" href="#skippy-function-index">↩ Back to function index</a>', ""]) | ||
|
|
||
| lines.extend([ '<a id="skippy-native-declarations"></a>', "## Native declarations", "", "The headers also define the following enums, structs, opaque handles, and ABI constants:", ""]) | ||
| for header in headers: | ||
| if header.declarations: | ||
| lines.append(f"- `{header.name}`: {', '.join(f'`{item}`' for item in header.declarations)}") | ||
| lines.extend( | ||
| [ | ||
| "", | ||
| "Source directory: `include/skippy/`. Regenerate this page after changing any public header or exported function.", | ||
| "", | ||
| ] | ||
| ) | ||
| return "\n".join(lines) | ||
|
|
||
|
|
||
| def main() -> int: | ||
| parser = argparse.ArgumentParser(description=__doc__) | ||
| repo_root = Path(__file__).resolve().parents[1] | ||
| parser.add_argument( | ||
| "--include-dir", | ||
| type=Path, | ||
| default=repo_root / ".deps/llama.cpp/include/skippy", | ||
| help="prepared patched llama.cpp Skippy header directory", | ||
| ) | ||
| parser.add_argument( | ||
| "--output", | ||
| type=Path, | ||
| default=repo_root / "website/src/docs/pages/skippy-api.md", | ||
| help="generated website Markdown path", | ||
| ) | ||
| parser.add_argument("--check", action="store_true", help="fail if the generated output differs") | ||
| args = parser.parse_args() | ||
|
|
||
| if not args.include_dir.is_dir(): | ||
| print( | ||
| f"Skippy headers not found at {args.include_dir}. Run scripts/prepare-llama.sh pinned first or pass --include-dir.", | ||
| file=sys.stderr, | ||
| ) | ||
| return 2 | ||
|
|
||
| paths = [args.include_dir.parent / "skippy.h", *sorted(args.include_dir.glob("*.h"))] | ||
| headers = [parse_header(path) for path in paths if path.exists()] | ||
| output = render(headers, args.include_dir) | ||
| if args.check: | ||
| current = args.output.read_text() if args.output.exists() else "" | ||
| return 0 if current == output else 1 | ||
| args.output.parent.mkdir(parents=True, exist_ok=True) | ||
| args.output.write_text(output) | ||
| print(f"generated {args.output} from {len(headers)} headers and {sum(len(h.functions) for h in headers)} functions") | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| raise SystemExit(main()) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ew
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haha