@xynogen/pix-9router 0.3.10 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/fetch.ts +6 -4
- package/src/format.test.ts +24 -0
- package/src/tool-schema.test.ts +20 -0
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ export ROUTER_API_KEY="your-key-here"
|
|
|
35
35
|
|
|
36
36
|
- **Provider**: on load, fetches `/models` from the router and registers them with Pi. The [modelgrep](https://modelgrep.com) catalog (via [`@xynogen/pix-data`](https://github.com/xynogen/pix-mono/tree/main/packages/pix-data)'s shared cache) is used internally to fill missing context window / modality fields where the router response omits them. Model list is cached at `~/.cache/pi/9router.json` (TTL 30 min).
|
|
37
37
|
- **fetch / search**: POST to `/web/fetch` and `/search` on the router (which proxies to exa). If the router is unreachable, falls back to `curl` (for `fetch`, raw URL fetch; for `search`, the same `/search` endpoint via curl). Tool output is rendered dimmed so fetched web content reads like faded context rather than primary output.
|
|
38
|
-
- **transcribe**: POST to `/audio/transcriptions` (Deepgram Nova 3 by default). Accepts any audio file path; falls back to curl.
|
|
38
|
+
- **transcribe**: POST to `/audio/transcriptions` (Deepgram Nova 3 by default). Accepts any audio file path; falls back to curl. Pass the optional `output_file` to write the full transcription to disk (parent dirs created, path pre-flight checked) and return only a short path summary to the model — useful for long results; without it the text is returned inline (truncated to 50,000 chars).
|
|
39
39
|
|
|
40
40
|
## Compact results
|
|
41
41
|
|
package/package.json
CHANGED
package/src/fetch.ts
CHANGED
|
@@ -241,10 +241,12 @@ export default function registerFetch(pi: ExtensionAPI): void {
|
|
|
241
241
|
),
|
|
242
242
|
parameters: Type.Object({
|
|
243
243
|
url: Type.String({ description: "URL to fetch" }),
|
|
244
|
-
format:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
format: Type.Optional(
|
|
245
|
+
StringEnum(["markdown", "text", "html"] as const, {
|
|
246
|
+
description:
|
|
247
|
+
'Optional choice. Enter exactly "markdown" for readable Markdown, "text" for plain text, or "html" for raw HTML. Default is "markdown".',
|
|
248
|
+
}),
|
|
249
|
+
),
|
|
248
250
|
max_characters: Type.Optional(
|
|
249
251
|
Type.Number({
|
|
250
252
|
description: "Max characters (default 1000, 0 = unlimited)",
|
package/src/format.test.ts
CHANGED
|
@@ -101,6 +101,30 @@ describe("fetch execution metadata", () => {
|
|
|
101
101
|
expect(result.content).toEqual(originalContent);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
it("defaults format to markdown when omitted (schema now optional)", async () => {
|
|
105
|
+
let sentFormat: unknown;
|
|
106
|
+
const raw = JSON.stringify({ url: "https://example.com", content: { text: "hello" } });
|
|
107
|
+
const result = await executeFetch(
|
|
108
|
+
// No `format` — valid now that the schema no longer requires it.
|
|
109
|
+
{ url: "https://example.com" },
|
|
110
|
+
undefined,
|
|
111
|
+
undefined,
|
|
112
|
+
{
|
|
113
|
+
apiPost: async (_path, body) => {
|
|
114
|
+
sentFormat = (body as { format?: unknown }).format;
|
|
115
|
+
return raw;
|
|
116
|
+
},
|
|
117
|
+
curl: async () => {
|
|
118
|
+
throw new Error("curl should not run");
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// The default flows through to both the API request and the metadata.
|
|
124
|
+
expect(sentFormat).toBe("markdown");
|
|
125
|
+
expect(result.details).toMatchObject({ outcome: "success", format: "markdown" });
|
|
126
|
+
});
|
|
127
|
+
|
|
104
128
|
it("reports curl fallback metadata while preserving the fallback banner", async () => {
|
|
105
129
|
const result = await executeFetch(
|
|
106
130
|
{ url: "https://example.com", format: "html", max_characters: 1000 },
|
package/src/tool-schema.test.ts
CHANGED
|
@@ -4,6 +4,7 @@ import registerSearch from "./search.ts";
|
|
|
4
4
|
|
|
5
5
|
function captureParameters(register: (pi: never) => void): {
|
|
6
6
|
properties: Record<string, { type?: string; enum?: string[]; description?: string }>;
|
|
7
|
+
required?: string[];
|
|
7
8
|
} {
|
|
8
9
|
let parameters: unknown;
|
|
9
10
|
register({
|
|
@@ -14,6 +15,7 @@ function captureParameters(register: (pi: never) => void): {
|
|
|
14
15
|
if (!parameters) throw new Error("tool parameters not captured");
|
|
15
16
|
return parameters as {
|
|
16
17
|
properties: Record<string, { type?: string; enum?: string[]; description?: string }>;
|
|
18
|
+
required?: string[];
|
|
17
19
|
};
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -38,3 +40,21 @@ describe("enum-like tool parameters", () => {
|
|
|
38
40
|
expect(format.description).toContain('"html"');
|
|
39
41
|
});
|
|
40
42
|
});
|
|
43
|
+
|
|
44
|
+
describe("fetch parameter requiredness", () => {
|
|
45
|
+
test("only url is required; format is optional and defaults to markdown", () => {
|
|
46
|
+
const params = captureParameters(registerFetch);
|
|
47
|
+
|
|
48
|
+
// url must be required, format must NOT be — this is the exact validator
|
|
49
|
+
// bug the optionality fix addresses (executeFetch already defaults format
|
|
50
|
+
// to "markdown"), so guard against a regression back to a required format.
|
|
51
|
+
expect(params.required).toEqual(["url"]);
|
|
52
|
+
expect(params.required).not.toContain("format");
|
|
53
|
+
|
|
54
|
+
// format stays a fully described enum in properties even while optional.
|
|
55
|
+
const format = params.properties.format;
|
|
56
|
+
if (!format) throw new Error("format schema not found");
|
|
57
|
+
expect(format.enum).toEqual(["markdown", "text", "html"]);
|
|
58
|
+
expect(format.description?.toLowerCase()).toContain("default");
|
|
59
|
+
});
|
|
60
|
+
});
|