@aliou/pi-synthetic 0.4.5 → 0.4.7

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/package.json CHANGED
@@ -1,36 +1,58 @@
1
1
  {
2
2
  "name": "@aliou/pi-synthetic",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "private": false,
7
+ "keywords": [
8
+ "pi-package",
9
+ "pi-extension",
10
+ "pi"
11
+ ],
4
12
  "repository": {
5
13
  "type": "git",
6
14
  "url": "https://github.com/aliou/pi-synthetic"
7
15
  },
8
- "keywords": [
9
- "pi-package"
10
- ],
11
16
  "pi": {
12
17
  "extensions": [
13
18
  "./src/index.ts"
14
19
  ],
15
20
  "video": "https://assets.aliou.me/pi-extensions/demos/pi-synthetic.mp4"
16
21
  },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "files": [
26
+ "src",
27
+ "README.md"
28
+ ],
17
29
  "peerDependencies": {
18
- "@mariozechner/pi-coding-agent": ">=0.52.7"
30
+ "@mariozechner/pi-coding-agent": ">=0.52.7",
31
+ "@mariozechner/pi-tui": ">=0.51.0"
19
32
  },
20
33
  "devDependencies": {
21
- "@biomejs/biome": "^2.3.13",
34
+ "@aliou/biome-plugins": "^0.3.2",
35
+ "@biomejs/biome": "^2.4.2",
22
36
  "@changesets/cli": "^2.27.11",
23
37
  "@mariozechner/pi-coding-agent": "0.52.7",
24
- "@mariozechner/pi-tui": "0.52.7",
25
38
  "@sinclair/typebox": "^0.34.48",
26
39
  "@types/node": "^25.0.10",
27
40
  "husky": "^9.1.7",
28
41
  "typescript": "^5.9.3"
29
42
  },
43
+ "peerDependenciesMeta": {
44
+ "@mariozechner/pi-coding-agent": {
45
+ "optional": true
46
+ },
47
+ "@mariozechner/pi-tui": {
48
+ "optional": true
49
+ }
50
+ },
30
51
  "scripts": {
31
52
  "typecheck": "tsc --noEmit",
32
53
  "lint": "biome check",
33
54
  "format": "biome check --write",
55
+ "check:lockfile": "pnpm install --frozen-lockfile --ignore-scripts",
34
56
  "changeset": "changeset",
35
57
  "version": "changeset version",
36
58
  "release": "pnpm changeset publish"
@@ -9,16 +9,6 @@ export function registerQuotasCommand(pi: ExtensionAPI): void {
9
9
  pi.registerCommand("synthetic:quotas", {
10
10
  description: "Display Synthetic API usage quotas",
11
11
  handler: async (_args, ctx) => {
12
- if (!ctx.hasUI) {
13
- const quotas = await fetchQuotas();
14
- if (!quotas) {
15
- console.error("Failed to fetch quotas");
16
- return;
17
- }
18
- console.log(formatQuotasPlain(quotas));
19
- return;
20
- }
21
-
22
12
  const result = await ctx.ui.custom<null>((tui, theme, _kb, done) => {
23
13
  let currentComponent: Component = new QuotasLoadingComponent(theme);
24
14
 
@@ -0,0 +1,104 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ import { SYNTHETIC_WEB_SEARCH_TOOL } from "../tools/search";
3
+
4
+ async function checkSubscriptionAccess(
5
+ apiKey: string,
6
+ ): Promise<{ ok: true } | { ok: false; reason: string }> {
7
+ try {
8
+ const response = await fetch("https://api.synthetic.new/v2/quotas", {
9
+ method: "GET",
10
+ headers: {
11
+ Authorization: `Bearer ${apiKey}`,
12
+ },
13
+ });
14
+
15
+ if (!response.ok) {
16
+ return {
17
+ ok: false,
18
+ reason: `Quotas check failed (HTTP ${response.status})`,
19
+ };
20
+ }
21
+
22
+ const data = await response.json();
23
+ if (data?.subscription?.limit > 0) {
24
+ return { ok: true };
25
+ }
26
+
27
+ return {
28
+ ok: false,
29
+ reason: "No active subscription (search requires a subscription plan)",
30
+ };
31
+ } catch (error) {
32
+ const message =
33
+ error instanceof Error ? error.message : "Unknown error occurred";
34
+ return { ok: false, reason: `Quotas check failed: ${message}` };
35
+ }
36
+ }
37
+
38
+ export function registerSyntheticWebSearchHooks(pi: ExtensionAPI): void {
39
+ let accessCheckPromise:
40
+ | Promise<{ ok: true } | { ok: false; reason: string }>
41
+ | undefined;
42
+ let hasAccess = false;
43
+ let deniedReason: string | undefined;
44
+ let didNotifyDenied = false;
45
+
46
+ // Keep tool inactive at session start. Availability is decided before each agent run.
47
+ pi.on("session_start", () => {
48
+ const current = pi.getActiveTools();
49
+ if (current.includes(SYNTHETIC_WEB_SEARCH_TOOL)) {
50
+ pi.setActiveTools(
51
+ current.filter((toolName) => toolName !== SYNTHETIC_WEB_SEARCH_TOOL),
52
+ );
53
+ }
54
+ });
55
+
56
+ // Verify subscription only when user starts agent execution.
57
+ pi.on("before_agent_start", async (_event, ctx) => {
58
+ const apiKey = process.env.SYNTHETIC_API_KEY;
59
+ if (!apiKey) {
60
+ hasAccess = false;
61
+ deniedReason = "SYNTHETIC_API_KEY is not configured";
62
+ accessCheckPromise = undefined;
63
+ } else {
64
+ if (deniedReason === "SYNTHETIC_API_KEY is not configured") {
65
+ deniedReason = undefined;
66
+ }
67
+
68
+ if (!hasAccess && !deniedReason) {
69
+ accessCheckPromise ??= checkSubscriptionAccess(apiKey);
70
+ const access = await accessCheckPromise;
71
+
72
+ if (!access.ok) {
73
+ deniedReason = access.reason;
74
+ } else {
75
+ hasAccess = true;
76
+ didNotifyDenied = false;
77
+ }
78
+ }
79
+ }
80
+
81
+ if (deniedReason) {
82
+ const current = pi.getActiveTools();
83
+ if (current.includes(SYNTHETIC_WEB_SEARCH_TOOL)) {
84
+ pi.setActiveTools(
85
+ current.filter((toolName) => toolName !== SYNTHETIC_WEB_SEARCH_TOOL),
86
+ );
87
+ }
88
+
89
+ if (ctx.hasUI && !didNotifyDenied) {
90
+ ctx.ui.notify(
91
+ `Synthetic web search disabled: ${deniedReason}`,
92
+ "warning",
93
+ );
94
+ didNotifyDenied = true;
95
+ }
96
+ return;
97
+ }
98
+
99
+ const current = pi.getActiveTools();
100
+ if (!current.includes(SYNTHETIC_WEB_SEARCH_TOOL)) {
101
+ pi.setActiveTools([...current, SYNTHETIC_WEB_SEARCH_TOOL]);
102
+ }
103
+ });
104
+ }
package/src/index.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
2
  import { registerQuotasCommand } from "./commands/quotas";
3
+ import { registerSyntheticWebSearchHooks } from "./hooks/search-tool-availability";
3
4
  import { registerSyntheticProvider } from "./providers/index";
4
5
  import { registerSyntheticWebSearchTool } from "./tools/search";
5
6
 
6
7
  export default async function (pi: ExtensionAPI) {
7
8
  registerSyntheticProvider(pi);
9
+ registerSyntheticWebSearchTool(pi);
10
+ registerSyntheticWebSearchHooks(pi);
8
11
 
9
- // Only register quotas command and web search tool if API key is available
10
12
  if (process.env.SYNTHETIC_API_KEY) {
11
13
  registerQuotasCommand(pi);
12
- registerSyntheticWebSearchTool(pi);
13
14
  }
14
15
  }
@@ -8,7 +8,8 @@ import type {
8
8
  import { Text } from "@mariozechner/pi-tui";
9
9
  import { type Static, Type } from "@sinclair/typebox";
10
10
 
11
- // Types
11
+ export const SYNTHETIC_WEB_SEARCH_TOOL = "synthetic_web_search" as const;
12
+
12
13
  interface SyntheticSearchResult {
13
14
  url: string;
14
15
  title: string;
@@ -27,7 +28,6 @@ interface WebSearchDetails {
27
28
  isError?: boolean;
28
29
  }
29
30
 
30
- // Schema
31
31
  const SearchParams = Type.Object({
32
32
  query: Type.String({
33
33
  description: "The search query. Be specific for best results.",
@@ -36,80 +36,9 @@ const SearchParams = Type.Object({
36
36
 
37
37
  type SearchParamsType = Static<typeof SearchParams>;
38
38
 
39
- // Check if API key has subscription access by calling quotas endpoint.
40
- // Returns "ok" if the user has access, or an error message if not.
41
- async function checkSubscriptionAccess(
42
- apiKey: string,
43
- ): Promise<{ ok: true } | { ok: false; reason: string }> {
44
- try {
45
- const response = await fetch("https://api.synthetic.new/v2/quotas", {
46
- method: "GET",
47
- headers: {
48
- Authorization: `Bearer ${apiKey}`,
49
- },
50
- });
51
-
52
- if (!response.ok) {
53
- return {
54
- ok: false,
55
- reason: `Quotas check failed (HTTP ${response.status})`,
56
- };
57
- }
58
-
59
- const data = await response.json();
60
- if (data?.subscription?.limit > 0) {
61
- return { ok: true };
62
- }
63
-
64
- return {
65
- ok: false,
66
- reason: "No active subscription (search requires a subscription plan)",
67
- };
68
- } catch (error) {
69
- const message =
70
- error instanceof Error ? error.message : "Unknown error occurred";
71
- return { ok: false, reason: `Quotas check failed: ${message}` };
72
- }
73
- }
74
-
75
- // Tool Registration
76
- export function registerSyntheticWebSearchTool(pi: ExtensionAPI) {
77
- const apiKey = process.env.SYNTHETIC_API_KEY;
78
- if (!apiKey) {
79
- return;
80
- }
81
-
82
- // Register tool immediately so it's available when tools are collected
83
- registerTool(pi, apiKey);
84
-
85
- // On session start, remove tool from active set, check subscription, re-add if valid
86
- pi.on("session_start", async (_event, ctx) => {
87
- // Disable tool until subscription is verified
88
- const activeTools = pi.getActiveTools();
89
- pi.setActiveTools(activeTools.filter((t) => t !== "synthetic_web_search"));
90
-
91
- const access = await checkSubscriptionAccess(apiKey);
92
- if (!access.ok) {
93
- if (ctx.hasUI) {
94
- ctx.ui.notify(
95
- `Synthetic web search disabled: ${access.reason}`,
96
- "warning",
97
- );
98
- }
99
- return;
100
- }
101
-
102
- // Add tool back to active tools
103
- const current = pi.getActiveTools();
104
- if (!current.includes("synthetic_web_search")) {
105
- pi.setActiveTools([...current, "synthetic_web_search"]);
106
- }
107
- });
108
- }
109
-
110
- function registerTool(pi: ExtensionAPI, apiKey: string) {
39
+ export function registerSyntheticWebSearchTool(pi: ExtensionAPI): void {
111
40
  pi.registerTool<typeof SearchParams, WebSearchDetails>({
112
- name: "synthetic_web_search",
41
+ name: SYNTHETIC_WEB_SEARCH_TOOL,
113
42
  label: "Synthetic: Web Search",
114
43
  description:
115
44
  "Search the web using Synthetic's zero-data-retention API. Returns search results with titles, URLs, content snippets, and publication dates. Use for finding documentation, articles, recent information, or any web content. Results are fresh and not cached by Synthetic.",
@@ -124,14 +53,21 @@ function registerTool(pi: ExtensionAPI, apiKey: string) {
124
53
  | undefined,
125
54
  _ctx: ExtensionContext,
126
55
  ): Promise<AgentToolResult<WebSearchDetails>> {
127
- // Send progress update
128
56
  onUpdate?.({
129
57
  content: [{ type: "text", text: "Searching..." }],
130
58
  details: { query: params.query },
131
59
  });
132
60
 
133
61
  try {
134
- // Make API request
62
+ const apiKey = process.env.SYNTHETIC_API_KEY;
63
+ if (!apiKey) {
64
+ const error = "SYNTHETIC_API_KEY is not configured";
65
+ return {
66
+ content: [{ type: "text", text: `Error: ${error}` }],
67
+ details: { error, isError: true },
68
+ };
69
+ }
70
+
135
71
  const response = await fetch("https://api.synthetic.new/v2/search", {
136
72
  method: "POST",
137
73
  headers: {
@@ -151,7 +87,6 @@ function registerTool(pi: ExtensionAPI, apiKey: string) {
151
87
  };
152
88
  }
153
89
 
154
- // Parse response
155
90
  let data: SyntheticSearchResponse;
156
91
  try {
157
92
  data = await response.json();
@@ -166,7 +101,6 @@ function registerTool(pi: ExtensionAPI, apiKey: string) {
166
101
  };
167
102
  }
168
103
 
169
- // Format results for LLM
170
104
  let content = `Found ${data.results.length} result(s):\n\n`;
171
105
  for (const result of data.results) {
172
106
  content += `## ${result.title}\n`;
@@ -184,7 +118,6 @@ function registerTool(pi: ExtensionAPI, apiKey: string) {
184
118
  },
185
119
  };
186
120
  } catch (error) {
187
- // Handle abort signal
188
121
  if (error instanceof Error && error.name === "AbortError") {
189
122
  return {
190
123
  content: [{ type: "text", text: "Search cancelled" }],
@@ -192,7 +125,6 @@ function registerTool(pi: ExtensionAPI, apiKey: string) {
192
125
  };
193
126
  }
194
127
 
195
- // Handle other errors
196
128
  const message =
197
129
  error instanceof Error ? error.message : "Unknown error occurred";
198
130
  return {
@@ -215,7 +147,6 @@ function registerTool(pi: ExtensionAPI, apiKey: string) {
215
147
  ): Text {
216
148
  const { expanded, isPartial } = options;
217
149
 
218
- // Handle partial/loading state
219
150
  if (isPartial) {
220
151
  const text =
221
152
  result.content?.[0]?.type === "text"
@@ -225,8 +156,6 @@ function registerTool(pi: ExtensionAPI, apiKey: string) {
225
156
  }
226
157
 
227
158
  const details = result.details;
228
-
229
- // Handle error state
230
159
  if (details?.isError) {
231
160
  const errorMsg =
232
161
  result.content?.[0]?.type === "text"
@@ -235,21 +164,18 @@ function registerTool(pi: ExtensionAPI, apiKey: string) {
235
164
  return new Text(theme.fg("error", errorMsg), 0, 0);
236
165
  }
237
166
 
238
- // Handle success state
239
167
  const results = details?.results || [];
240
168
  let text = theme.fg("success", `✓ Found ${results.length} result(s)`);
241
169
 
242
- // Collapsed view
243
170
  if (!expanded && results.length > 0) {
244
171
  const first = results[0];
245
172
  text += `\n ${theme.fg("dim", `${first.title}`)}`;
246
173
  if (results.length > 1) {
247
174
  text += theme.fg("dim", ` (${results.length - 1} more)`);
248
175
  }
249
- text += theme.fg("muted", ` [Ctrl+O to expand]`);
176
+ text += theme.fg("muted", " [Ctrl+O to expand]");
250
177
  }
251
178
 
252
- // Expanded view
253
179
  if (expanded) {
254
180
  for (const r of results) {
255
181
  text += `\n\n${theme.fg("accent", theme.bold(r.title))}`;
@@ -1,11 +0,0 @@
1
- {
2
- "$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
3
- "changelog": "@changesets/cli/changelog",
4
- "commit": false,
5
- "fixed": [],
6
- "linked": [],
7
- "access": "public",
8
- "baseBranch": "main",
9
- "updateInternalDependencies": "patch",
10
- "ignore": []
11
- }
@@ -1,30 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- pull_request:
5
-
6
- concurrency:
7
- group: ${{ github.workflow }}-${{ github.ref }}
8
- cancel-in-progress: true
9
-
10
- jobs:
11
- check:
12
- runs-on: ubuntu-latest
13
- steps:
14
- - uses: actions/checkout@v4
15
-
16
- - uses: pnpm/action-setup@v4
17
-
18
- - uses: actions/setup-node@v4
19
- with:
20
- node-version: "22"
21
- cache: "pnpm"
22
-
23
- - name: Install dependencies
24
- run: pnpm install --frozen-lockfile
25
-
26
- - name: Lint
27
- run: pnpm lint
28
-
29
- - name: Typecheck
30
- run: pnpm typecheck
@@ -1,151 +0,0 @@
1
- name: Publish
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
- workflow_dispatch:
8
- inputs:
9
- skip-checks:
10
- description: "Skip lint and typecheck"
11
- type: boolean
12
- default: false
13
-
14
- concurrency:
15
- group: ${{ github.workflow }}-${{ github.ref }}
16
- cancel-in-progress: true
17
-
18
- jobs:
19
- check:
20
- if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.skip-checks) }}
21
- runs-on: ubuntu-latest
22
- steps:
23
- - uses: actions/checkout@v4
24
-
25
- - uses: pnpm/action-setup@v4
26
-
27
- - uses: actions/setup-node@v4
28
- with:
29
- node-version: "22"
30
- cache: "pnpm"
31
-
32
- - name: Install dependencies
33
- run: pnpm install --frozen-lockfile
34
-
35
- - name: Lint
36
- run: pnpm lint
37
-
38
- - name: Typecheck
39
- run: pnpm typecheck
40
-
41
- publish:
42
- name: Publish
43
- needs: check
44
- if: ${{ always() && (needs.check.result == 'success' || needs.check.result == 'skipped') }}
45
- runs-on: ubuntu-latest
46
- permissions:
47
- contents: write
48
- packages: write
49
- pull-requests: write
50
- id-token: write
51
-
52
- steps:
53
- - name: Checkout
54
- uses: actions/checkout@v4
55
- with:
56
- fetch-depth: 0
57
-
58
- - name: Setup pnpm
59
- uses: pnpm/action-setup@v4
60
-
61
- - name: Setup Node.js
62
- uses: actions/setup-node@v4
63
- with:
64
- node-version: "22"
65
- registry-url: "https://registry.npmjs.org"
66
- scope: "@aliou"
67
- cache: "pnpm"
68
-
69
- - name: Upgrade npm for OIDC support
70
- run: npm install -g npm@latest
71
-
72
- - name: Install dependencies
73
- run: pnpm install --frozen-lockfile
74
-
75
- - name: Get release info
76
- id: release-info
77
- run: |
78
- pnpm changeset status --output=release.json 2>/dev/null || echo '{"releases":[]}' > release.json
79
- node <<NODE
80
- const fs = require('fs');
81
- const release = JSON.parse(fs.readFileSync('release.json', 'utf8'));
82
- const releases = release.releases?.filter(r => r.type !== 'none') || [];
83
-
84
- let title = 'Version Packages';
85
- let commit = 'Version Packages';
86
- if (releases.length === 1) {
87
- const { name, newVersion } = releases[0];
88
- title = 'Updating ' + name + ' to version ' + newVersion;
89
- commit = name + '@' + newVersion;
90
- } else if (releases.length > 1) {
91
- const summary = releases.map(r => r.name + '@' + r.newVersion).join(', ');
92
- title = 'Updating ' + summary;
93
- commit = summary;
94
- }
95
-
96
- fs.appendFileSync(process.env.GITHUB_OUTPUT, 'title=' + title + '\n');
97
- fs.appendFileSync(process.env.GITHUB_OUTPUT, 'commit=' + commit + '\n');
98
- NODE
99
- rm -f release.json
100
- continue-on-error: true
101
-
102
- - name: Create Release PR or Publish
103
- id: changesets
104
- uses: changesets/action@v1
105
- with:
106
- version: pnpm changeset version
107
- publish: pnpm changeset publish
108
- title: ${{ steps.release-info.outputs.title || 'Version Packages' }}
109
- commit: ${{ steps.release-info.outputs.commit || 'Version Packages' }}
110
- env:
111
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
112
- NPM_CONFIG_PROVENANCE: true
113
-
114
- - name: Create GitHub releases
115
- if: steps.changesets.outputs.published == 'true'
116
- env:
117
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
118
- PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }}
119
- run: |
120
- node <<'NODE'
121
- const { execSync } = require("node:child_process");
122
-
123
- const published = JSON.parse(process.env.PUBLISHED_PACKAGES || "[]");
124
-
125
- for (const pkg of published) {
126
- const shortName = pkg.name.replace(/^@[^/]+\//, "");
127
- const tag = `${shortName}@${pkg.version}`;
128
-
129
- const existing = execSync(`git tag --list ${tag}`, { encoding: "utf8" }).trim();
130
- if (!existing) {
131
- execSync(`git tag ${tag}`);
132
- execSync(`git push origin ${tag}`);
133
- }
134
-
135
- let hasRelease = false;
136
- try {
137
- const output = execSync(`gh release view ${tag} --json tagName --jq .tagName`, {
138
- stdio: ["ignore", "pipe", "ignore"],
139
- }).toString().trim();
140
- hasRelease = output.length > 0;
141
- } catch {
142
- hasRelease = false;
143
- }
144
-
145
- if (!hasRelease) {
146
- execSync(`gh release create ${tag} --title ${tag} --notes "Release ${tag}"`, {
147
- stdio: "inherit",
148
- });
149
- }
150
- }
151
- NODE
package/.husky/pre-commit DELETED
@@ -1,3 +0,0 @@
1
- pnpm run typecheck
2
- pnpm run lint
3
- pnpm run format
package/AGENTS.md DELETED
@@ -1,69 +0,0 @@
1
- # pi-synthetic
2
-
3
- Public Pi extension providing open-source language models via Synthetic's API. People could be using this, so consider backwards compatibility when making changes.
4
-
5
- Pi is pre-1.0.0, so breaking changes can happen between Pi versions. This extension must stay up to date with Pi or things will break.
6
-
7
- ## Stack
8
-
9
- - TypeScript (strict mode)
10
- - pnpm 10.26.1
11
- - Biome for linting/formatting
12
- - Changesets for versioning
13
-
14
- ## Scripts
15
-
16
- ```bash
17
- pnpm typecheck # Type check
18
- pnpm lint # Lint (runs on pre-commit)
19
- pnpm format # Format
20
- pnpm changeset # Create changeset for versioning
21
- ```
22
-
23
- ## Structure
24
-
25
- ```
26
- src/
27
- index.ts # Extension entry, registers provider
28
- providers/
29
- index.ts # Provider registration
30
- models.ts # Hardcoded model definitions
31
- ```
32
-
33
- ## Conventions
34
-
35
- - API key comes from environment (`SYNTHETIC_API_KEY`)
36
- - Uses OpenAI-compatible API at `https://api.synthetic.new/openai/v1`
37
- - Models are hardcoded in `src/providers/models.ts`
38
- - Update model list when Synthetic adds new models
39
-
40
- ## Adding Models
41
-
42
- Edit `src/providers/models.ts`:
43
-
44
- ```typescript
45
- {
46
- id: "hf:vendor/model-name",
47
- name: "vendor/model-name",
48
- reasoning: true/false,
49
- input: ["text"] or ["text", "image"],
50
- cost: {
51
- input: 0.55, // $ per million tokens
52
- output: 2.19,
53
- cacheRead: 0.55,
54
- cacheWrite: 0
55
- },
56
- contextWindow: 202752,
57
- maxTokens: 65536
58
- }
59
- ```
60
-
61
- Get pricing from `https://api.synthetic.new/openai/v1/models`.
62
-
63
- ## Versioning
64
-
65
- Uses changesets. Run `pnpm changeset` before committing user-facing changes.
66
-
67
- - `patch`: bug fixes, model updates
68
- - `minor`: new models, features
69
- - `major`: breaking changes
package/CHANGELOG.md DELETED
@@ -1,84 +0,0 @@
1
- # @aliou/pi-synthetic
2
-
3
- ## 0.4.5
4
-
5
- ### Patch Changes
6
-
7
- - 7489bc0: update model list: add nvidia/Kimi-K2.5-NVFP4, remove 6 discontinued models
8
-
9
- ## 0.4.4
10
-
11
- ### Patch Changes
12
-
13
- - 86a3145: Fix quotas command showing duplicate notification in TUI mode
14
- - f94cc6b: fix: register search tool at init time so it's available when pi collects tools
15
-
16
- ## 0.4.3
17
-
18
- ### Patch Changes
19
-
20
- - 7dc1d80: Defer subscription check to session_start for non-blocking extension init.
21
-
22
- ## 0.4.2
23
-
24
- ### Patch Changes
25
-
26
- - d9af905: Add demo video URL for the Pi package browser.
27
-
28
- ## 0.4.1
29
-
30
- ### Patch Changes
31
-
32
- - aba3bb8: fix: use correct /v2/quotas endpoint for subscription access check
33
-
34
- ## 0.4.0
35
-
36
- ### Minor Changes
37
-
38
- - 5cca252: Add `/synthetic:quotas` command to display API usage quotas
39
-
40
- A new slash command that shows your Synthetic API subscription quotas in a rich terminal UI:
41
-
42
- - Visual usage bar with color-coded severity (green/yellow/red based on usage)
43
- - Aligned columns showing limit, used, and remaining requests
44
- - ISO8601 renewal timestamp with relative time formatting (e.g., "in 5 hours")
45
- - Closes on any key press
46
-
47
- The command is only registered when `SYNTHETIC_API_KEY` environment variable is set.
48
-
49
- - a8cacfb: Add Synthetic web search tool
50
-
51
- New tool `synthetic_web_search` allows agents to search the web using Synthetic's zero-data-retention API. Returns search results with titles, URLs, content snippets, and publication dates.
52
-
53
- **Note:** Search is a subscription-only feature. The tool will only be registered if the `SYNTHETIC_API_KEY` belongs to an active subscription (verified via the usage endpoint).
54
-
55
- ## 0.3.0
56
-
57
- ### Minor Changes
58
-
59
- - 5f67daf: Switch from Anthropic to OpenAI API endpoints
60
-
61
- - Change API endpoint from `/anthropic` to `/openai/v1`
62
- - Update from `anthropic-messages` to `openai-completions` API
63
- - Add compatibility flags for proper role handling (`supportsDeveloperRole: false`)
64
- - Use standard `max_tokens` field instead of `max_completion_tokens`
65
-
66
- ## 0.2.0
67
-
68
- ### Minor Changes
69
-
70
- - 58d21ca: Fix model configurations from Synthetic API
71
-
72
- - Update maxTokens for all Synthetic models using values from models.dev (synthetic provider)
73
- - Fix Kimi-K2-Instruct-0905 reasoning flag to false
74
-
75
- ## 0.1.0
76
-
77
- ### Minor Changes
78
-
79
- - 4a32d18: Initial release with 19 open-source models
80
-
81
- - Add Synthetic provider with Anthropic-compatible API
82
- - Support for DeepSeek, Qwen, MiniMax, Kimi, Llama, GLM models
83
- - Vision and reasoning capabilities where available
84
- - Hardcoded model definitions with per-token pricing
package/biome.json DELETED
@@ -1,30 +0,0 @@
1
- {
2
- "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
3
- "vcs": {
4
- "enabled": true,
5
- "clientKind": "git",
6
- "useIgnoreFile": true
7
- },
8
- "files": {
9
- "includes": ["**/*.ts", "**/*.json"],
10
- "ignoreUnknown": true
11
- },
12
- "assist": {
13
- "actions": {
14
- "source": {
15
- "organizeImports": "on"
16
- }
17
- }
18
- },
19
- "linter": {
20
- "enabled": true,
21
- "rules": {
22
- "recommended": true
23
- }
24
- },
25
- "formatter": {
26
- "enabled": true,
27
- "indentStyle": "space",
28
- "indentWidth": 2
29
- }
30
- }
package/shell.nix DELETED
@@ -1,10 +0,0 @@
1
- {
2
- pkgs ? import <nixpkgs> { },
3
- }:
4
-
5
- pkgs.mkShell {
6
- buildInputs = with pkgs; [
7
- nodejs
8
- pnpm_10
9
- ];
10
- }
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "resolveJsonModule": true,
11
- "noEmit": true
12
- },
13
- "include": ["src/**/*"],
14
- "exclude": ["node_modules"]
15
- }