pi-qtools 0.1.0
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/LICENSE +21 -0
- package/README.md +159 -0
- package/extensions/qtools.ts +1528 -0
- package/package.json +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 proyas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# pi-qtools
|
|
2
|
+
|
|
3
|
+
Bridge between [pi](https://pi.dev) and the **Qwen Token Plan** subscription.
|
|
4
|
+
|
|
5
|
+
The plan ships server-side tools and media models that pi cannot reach on its own:
|
|
6
|
+
pi talks `/chat/completions` to a chat model, while these live on `/responses`, on
|
|
7
|
+
native `/api/v1/services/...` endpoints, or behind async task polling. This package
|
|
8
|
+
exposes them as pi tools *and* slash commands.
|
|
9
|
+
|
|
10
|
+
Requires a `qwen-token-plan*` provider authenticated in pi (`/login`).
|
|
11
|
+
|
|
12
|
+
## Tools (agent-callable)
|
|
13
|
+
|
|
14
|
+
| Tool | Backing capability | Notes |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `qwen_web_search` | `/responses` `web_search` | answer + queries issued + source URLs |
|
|
17
|
+
| `qwen_web_extractor` | `/responses` `web_search` + `web_extractor` | page text filtered to a stated goal; the API requires the search tool alongside |
|
|
18
|
+
| `qwen_code_interpreter` | `/responses` `code_interpreter` | returns the Python it ran, container id, real stdout |
|
|
19
|
+
| `qwen_image_search` | `/responses` `web_search_image` | text → image URLs. **qwen3.8-max only** |
|
|
20
|
+
| `qwen_reverse_image_search` | `/responses` `image_search` | image URL or local file → similar images; accepts a local path and inlines it as a data URL (4 MB cap) |
|
|
21
|
+
| `qwen_deep_search` | chat `search_strategy: "agent"` | multi-step research; very expensive (~340k prompt tokens observed) |
|
|
22
|
+
| `qwen_generate_image` | native `multimodal-generation` | `wan2.7-image`, `wan2.7-image-pro`, `qwen-image-3.0-pro`; sync; `save: true` writes into `<cwd>/out/qtools/` |
|
|
23
|
+
| `qwen_generate_video` | native `video-generation` + task polling | `happyhorse-1.1-t2v` / `-i2v` / `-r2v`; async, ~90-120s; `save: true` writes the mp4 |
|
|
24
|
+
|
|
25
|
+
## Passive main-loop search
|
|
26
|
+
|
|
27
|
+
When the active model is a Qwen plan chat model, `enable_search` is injected into
|
|
28
|
+
pi's own requests, so the model can search without calling a tool. Modes:
|
|
29
|
+
|
|
30
|
+
| mode | cost/turn | behaviour |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `off` | 0 | memory only |
|
|
33
|
+
| `turbo` (default) | ~330 tok | searches, fresh answers |
|
|
34
|
+
| `max` | ~420 tok | wider search + inline `[n]` citations |
|
|
35
|
+
|
|
36
|
+
`search_strategy: "agent"` and `enable_code_interpreter` are **never** injected:
|
|
37
|
+
both flip Alibaba "Agent mode", which rejects any `tools` array, and pi always
|
|
38
|
+
sends one. That is why those two capabilities are standalone tools instead.
|
|
39
|
+
|
|
40
|
+
## Commands
|
|
41
|
+
|
|
42
|
+
- `/qtools-config` — settings TUI (search mode, model defaults, capability matrix)
|
|
43
|
+
- `/qimage <prompt> [-m model] [-s WxH]` — saves into `out/qtools/`
|
|
44
|
+
- `/qvideo <prompt> [-i <path|url>] [-m model] [-s WxH]` — `-i` selects image-to-video; saves into `out/qtools/`
|
|
45
|
+
|
|
46
|
+
### Getting parameter hints
|
|
47
|
+
|
|
48
|
+
pi exposes no `argumentHint` for extension commands, so `/qimage` and `/qvideo` make
|
|
49
|
+
themselves discoverable three ways:
|
|
50
|
+
|
|
51
|
+
1. their **signature is in the command description**, visible in the `/` picker
|
|
52
|
+
2. **`--help`** (or running with no prompt) prints every flag, the allowed values, the
|
|
53
|
+
active default and the exact output path
|
|
54
|
+
3. **argument completion**: after `/qimage ` or on a partial flag, suggestions list
|
|
55
|
+
`-m`/`-s`/`-i`/`--help`, and after `-m `/`-s ` they list the valid models/sizes
|
|
56
|
+
|
|
57
|
+
Completions are suppressed while a prompt is being typed, because pi replaces the
|
|
58
|
+
whole argument region with the chosen value — offering a flag mid-prompt would
|
|
59
|
+
delete what was already written.
|
|
60
|
+
|
|
61
|
+
## Video generation schema (measured)
|
|
62
|
+
|
|
63
|
+
All three models post to `/api/v1/services/aigc/video-generation/video-synthesis`
|
|
64
|
+
with `X-DashScope-Async: enable`, then poll `GET /api/v1/tasks/{id}`.
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"model": "happyhorse-1.1-i2v",
|
|
69
|
+
"input": {
|
|
70
|
+
"prompt": "the pig blinks slowly",
|
|
71
|
+
"media": [{ "type": "first_frame", "url": "https://... or data:image/png;base64,..." }]
|
|
72
|
+
},
|
|
73
|
+
"parameters": { "size": "1280*720" }
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| fact | value |
|
|
78
|
+
|---|---|
|
|
79
|
+
| required field | `input.media` (an array; `img_url` / `image_url` / `ref_url` are all ignored) |
|
|
80
|
+
| `media[].type` for i2v | exactly `"first_frame"` |
|
|
81
|
+
| `media[].type` for r2v | exactly `"reference_image"` |
|
|
82
|
+
| t2v | takes no `media` at all |
|
|
83
|
+
| status of each mode | t2v, i2v and r2v all reached `SUCCEEDED` end to end |
|
|
84
|
+
| minimum source image | **300x300** |
|
|
85
|
+
| local files | supported by inlining a `data:` URL; verified with a 5.0 MB PNG (6.7 MB base64) |
|
|
86
|
+
| output resolution | follows the **source aspect ratio**; `size` is advisory for i2v/r2v (a square frame returned 1440x1440) |
|
|
87
|
+
|
|
88
|
+
**Critical gotcha:** the submit call returns **HTTP 200 with a `task_id` even when the
|
|
89
|
+
request is wrong**. Every shape above was discovered by polling a *failed* task and
|
|
90
|
+
reading `output.message`, not by reading the submit response. Never treat a successful
|
|
91
|
+
submit as a successful generation — this tool surfaces `status` and `detail` for that reason.
|
|
92
|
+
|
|
93
|
+
Image generation sizes must be **589824-16777216 total pixels** (768² to 4096²); `512*512`
|
|
94
|
+
is rejected.
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
Persisted to `<agentDir>/qtools.json` (migrated automatically from the older
|
|
99
|
+
`qwen-tools.json` on first load):
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"searchMode": "turbo",
|
|
104
|
+
"fallbackModel": "qwen3.7-max",
|
|
105
|
+
"showReasoning": false,
|
|
106
|
+
"deepSearchMaxTokens": 16384,
|
|
107
|
+
"imageModel": "wan2.7-image",
|
|
108
|
+
"videoModel": "happyhorse-1.1-t2v"
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Verified capability matrix
|
|
113
|
+
|
|
114
|
+
Measured against the live endpoint rather than taken from docs, because the two
|
|
115
|
+
surfaces differ per model:
|
|
116
|
+
|
|
117
|
+
| model | chat search | agent | chat code_int | `/responses` tools | `web_search_image` | `image_search` |
|
|
118
|
+
|---|---|---|---|---|---|---|
|
|
119
|
+
| qwen3.8-max | yes | no | no | all | **yes** | yes |
|
|
120
|
+
| qwen3.8-flash | yes | no | no | s/c/e | no | yes |
|
|
121
|
+
| qwen3.7-max | yes | yes | yes | s/c/e | no | no |
|
|
122
|
+
| qwen3.7-plus | yes | yes | yes | s/c/e | no | yes |
|
|
123
|
+
| qwen3.6-flash | yes | yes | yes | s/c/e | no | yes |
|
|
124
|
+
| deepseek-v4-* | yes | yes | yes | s/c/e | no | no |
|
|
125
|
+
| glm-5.2 | **no** | no | no | s/c/e | no | no |
|
|
126
|
+
|
|
127
|
+
`glm-5.2` rejects `enable_search` with a hard error, so injection is skipped for it;
|
|
128
|
+
without that guard every turn would fail after a model switch.
|
|
129
|
+
|
|
130
|
+
## Known gaps
|
|
131
|
+
|
|
132
|
+
- **TTS / ASR are not implemented.** `qwen-audio-3.0-tts-plus` resolves as a model on
|
|
133
|
+
`/api/v1/services/audio/tts/SpeechSynthesizer` but the engine returns
|
|
134
|
+
`InvalidParameter [cosyvoice:] Engine error [411]: TTS speak operation failed` for
|
|
135
|
+
every request shape tried (plain, `format`/`sample_rate`, `parameters`, and with
|
|
136
|
+
`X-DashScope-SSE: enable`). `/api/v1/services/audio/asr/transcription` reports it
|
|
137
|
+
needs async on one call and `url error` when given the async header. The correct
|
|
138
|
+
request contract is still unknown — likely WebSocket-only.
|
|
139
|
+
- `happyhorse-1.1-r2v` reaches `SUCCEEDED` with `media[].type: "reference_image"`, so the
|
|
140
|
+
request shape is confirmed. Whether the output faithfully follows the reference was not
|
|
141
|
+
checked (that needs vision comparison, which this package does not add).
|
|
142
|
+
- Realtime voice (`qwen-audio-3.0-realtime-plus`) is intentionally out of scope.
|
|
143
|
+
- pi's model catalogue labels `qwen3.7-plus` as `input: ['text','image']`, but the
|
|
144
|
+
gateway rejects image input on it ("only supports text modality"). Upstream bug.
|
|
145
|
+
- Console capability labels differ from API `tools[].type` strings. The Qwen UI says
|
|
146
|
+
`t2i_search`/`i2i_search`; the API wants `web_search_image`/`image_search`. Sending
|
|
147
|
+
a label is **silently ignored** — the endpoint accepts any type string, including
|
|
148
|
+
nonsense, so "no error" does not mean "supported". `/qtools-config` lists the mapping.
|
|
149
|
+
|
|
150
|
+
## Development
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pi install ./path/to/qtools # loads from this path, no copy
|
|
154
|
+
pi -e ./extensions/qtools.ts "prompt" # one-off load
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT
|