pi-qtools
Qwen Token Plan bridge for pi: web search, scraping, code interpreter, image search, and image/video generation via DashScope built-in tools and native task endpoints.
Package details
Install pi-qtools from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-qtools- Package
pi-qtools- Version
0.2.0- Published
- Sep 27, 2026
- Downloads
- 281/mo · 281/wk
- Author
- proyas21
- License
- MIT
- Types
- extension
- Size
- 72.9 KB
- Dependencies
- 0 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./extensions/qtools.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-qtools
Bridge between pi and the Qwen Token Plan subscription.
The plan ships server-side tools and media models that pi cannot reach on its own:
pi talks /chat/completions to a chat model, while these live on /responses, on
native /api/v1/services/... endpoints, or behind async task polling. This package
exposes them as pi tools and slash commands.
Requires a qwen-token-plan* provider authenticated in pi (/login).
Tools (agent-callable)
| Tool | Backing capability | Notes |
|---|---|---|
qwen_web_search |
/responses web_search |
answer + queries issued + source URLs |
qwen_web_extractor |
/responses web_search + web_extractor |
page text filtered to a stated goal; the API requires the search tool alongside |
qwen_code_interpreter |
/responses code_interpreter |
returns the Python it ran, container id, real stdout |
qwen_image_search |
/responses web_search_image |
text → image URLs. qwen3.8-max only |
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) |
qwen_deep_search |
chat search_strategy: "agent" |
multi-step research; very expensive (~340k prompt tokens observed) |
qwen_generate_image |
native multimodal-generation |
wan2.7-image, wan2.7-image-pro, qwen-image-3.0-pro; sync; image edits a reference picture; save: true writes into <cwd>/out/qtools/ |
qwen_generate_video |
native video-generation + task polling |
happyhorse-1.1-t2v / -i2v / -r2v; async, ~90-120s; save: true writes the mp4 |
Passive main-loop search
When the active model is a Qwen plan chat model, enable_search is injected into
pi's own requests, so the model can search without calling a tool. Modes:
| mode | cost/turn | behaviour |
|---|---|---|
off |
0 | memory only |
turbo (default) |
~330 tok | searches, fresh answers |
max |
~420 tok | wider search + inline [n] citations |
search_strategy: "agent" and enable_code_interpreter are never injected:
both flip Alibaba "Agent mode", which rejects any tools array, and pi always
sends one. That is why those two capabilities are standalone tools instead.
Commands
/qtools-config— settings TUI (search mode, model defaults, capability matrix)/qimage <prompt> [-i img] [-m model] [-s WxH]— saves intoout/qtools//qvideo <prompt> [-i <path|url>] [-m model] [-s WxH]—-iselects image-to-video; saves intoout/qtools/
Image editing (measured)
Passing a reference image turns the same endpoint from text-to-image into image+prompt editing. The shape is one extra content part:
{ "input": { "messages": [ { "role": "user", "content": [
{ "image": "https://... or data:image/png;base64,..." },
{ "text": "turn this into a red panda, keep the flat vector style" }
] } ] } }
Verified: a flat-vector pig + that prompt came back a red panda in the same style, and a local file inlined as a data URL worked too. Two reference images in one message are accepted.
The trap here is that a wrong request shape still returns a valid image, just
generated from the text alone. Every claim above was checked by asking a vision
model (qwen3.8-max, which has real image input on this plan) to describe the
output, not by trusting the HTTP 200.
Getting parameter hints
pi exposes no argumentHint for extension commands, so /qimage and /qvideo make
themselves discoverable three ways:
- their signature is in the command description, visible in the
/picker --help(or running with no prompt) prints every flag, the allowed values, the active default and the exact output path- argument completion: after
/qimageor on a partial flag, suggestions list-m/-s/-i/--help, and after-m/-sthey list the valid models/sizes
Completions are suppressed while a prompt is being typed, because pi replaces the whole argument region with the chosen value — offering a flag mid-prompt would delete what was already written.
Video generation schema (measured)
All three models post to /api/v1/services/aigc/video-generation/video-synthesis
with X-DashScope-Async: enable, then poll GET /api/v1/tasks/{id}.
{
"model": "happyhorse-1.1-i2v",
"input": {
"prompt": "the pig blinks slowly",
"media": [{ "type": "first_frame", "url": "https://... or data:image/png;base64,..." }]
},
"parameters": { "size": "1280*720" }
}
| fact | value |
|---|---|
| required field | input.media (an array; img_url / image_url / ref_url are all ignored) |
media[].type for i2v |
exactly "first_frame" |
media[].type for r2v |
exactly "reference_image" |
| t2v | takes no media at all |
| status of each mode | t2v, i2v and r2v all reached SUCCEEDED end to end |
| minimum source image | 300x300 |
| local files | supported by inlining a data: URL; verified with a 5.0 MB PNG (6.7 MB base64) |
| output resolution | follows the source aspect ratio; size is advisory for i2v/r2v (a square frame returned 1440x1440) |
Critical gotcha: the submit call returns HTTP 200 with a task_id even when the
request is wrong. Every shape above was discovered by polling a failed task and
reading output.message, not by reading the submit response. Never treat a successful
submit as a successful generation — this tool surfaces status and detail for that reason.
Image generation sizes must be 589824-16777216 total pixels (768² to 4096²); 512*512
is rejected.
Configuration
Persisted to <agentDir>/qtools.json (migrated automatically from the older
qwen-tools.json on first load):
{
"searchMode": "turbo",
"fallbackModel": "qwen3.7-max",
"showReasoning": false,
"deepSearchMaxTokens": 16384,
"imageModel": "wan2.7-image",
"videoModel": "happyhorse-1.1-t2v"
}
Verified capability matrix
Measured against the live endpoint rather than taken from docs, because the two surfaces differ per model:
| model | chat search | agent | chat code_int | /responses tools |
web_search_image |
image_search |
|---|---|---|---|---|---|---|
| qwen3.8-max | yes | no | no | all | yes | yes |
| qwen3.8-flash | yes | no | no | s/c/e | no | yes |
| qwen3.7-max | yes | yes | yes | s/c/e | no | no |
| qwen3.7-plus | yes | yes | yes | s/c/e | no | yes |
| qwen3.6-flash | yes | yes | yes | s/c/e | no | yes |
| deepseek-v4-* | yes | yes | yes | s/c/e | no | no |
| glm-5.2 | no | no | no | s/c/e | no | no |
glm-5.2 rejects enable_search with a hard error, so injection is skipped for it;
without that guard every turn would fail after a model switch.
Known gaps
- TTS / ASR are not implemented.
qwen-audio-3.0-tts-plusresolves as a model on/api/v1/services/audio/tts/SpeechSynthesizerbut the engine returnsInvalidParameter [cosyvoice:] Engine error [411]: TTS speak operation failedfor every request shape tried (plain,format/sample_rate,parameters, and withX-DashScope-SSE: enable)./api/v1/services/audio/asr/transcriptionreports it needs async on one call andurl errorwhen given the async header. The correct request contract is still unknown — likely WebSocket-only. happyhorse-1.1-r2vreachesSUCCEEDEDwithmedia[].type: "reference_image", so the request shape is confirmed. Whether the output faithfully follows the reference was not checked (that needs vision comparison, which this package does not add).- Realtime voice (
qwen-audio-3.0-realtime-plus) is intentionally out of scope. - pi's model catalogue labels
qwen3.7-plusasinput: ['text','image'], but the gateway rejects image input on it ("only supports text modality"). Upstream bug. - Console capability labels differ from API
tools[].typestrings. The Qwen UI sayst2i_search/i2i_search; the API wantsweb_search_image/image_search. Sending a label is silently ignored — the endpoint accepts any type string, including nonsense, so "no error" does not mean "supported"./qtools-configlists the mapping.
Development
pi install ./path/to/qtools # loads from this path, no copy
pi -e ./extensions/qtools.ts "prompt" # one-off load
License
MIT