docs, site: Integrating your app or agent with magpie, at docs/integrating.md and usemagpie.ai/docs/integrate (#918, Magixyne)
A developer of an AI app asked how to work with magpie and whether an app can register itself after one confirmation. The page says what exists: the gateway (find it via /api/hello and settings.json's port, the OpenAI, Anthropic, Responses and Gemini endpoints, /v1/models' fields), how a key magpie-<id> or the User-Agent's first word names the app in Usage, a row on the Agents page by an adapter PR in internal/agent (config-edit, Cindy-style own import link confirmed in the app, or a local API) with what the PR needs, what plugins can and can't do, and what isn't there: no link or API registers an app or installs a plugin, and magpie://import adds providers only. English and Chinese pages, linked from every docs page's nav and footer. TestThirdPartyAppIsCountedAsItself pins the attribution the page promises. go vet and the gateway tests pass; site node tests pass; pages checked in headless Chromium (no errors, TOC, no horizontal scroll at 390px).
This commit is contained in:
1 parent
47f0824c6b
commit
df95ac6bc2
17 files changed
+578
No files matched your search
@@ -0,0 +1,145 @@
|
||||
# Integrating your app or agent with magpie
|
||||
|
||||
For developers of an AI app, agent or client who want it to work well with
|
||||
magpie. There are three levels, from no work on magpie's side to a row of
|
||||
your own on the Agents page. The web version is
|
||||
<https://usemagpie.ai/docs/integrate>.
|
||||
|
||||
| You want | Route | Change to magpie |
|
||||
| ----------------------------------------------------- | ------------------------------ | ---------------- |
|
||||
| Your app's requests served by magpie, counted under its own name in Usage | [Talk to the gateway](#1-talk-to-the-gateway) | none |
|
||||
| A row on the Agents page: detected, its model picked there, put back on disconnect | [An agent adapter](#2-a-row-on-the-agents-page) | a pull request |
|
||||
| A new model source (a subscription, a vendor), or rewriting requests | [A plugin](#3-plugins) | none |
|
||||
|
||||
## 1. Talk to the gateway
|
||||
|
||||
magpie runs a local LLM gateway. Every model the user has in magpie
|
||||
(subscriptions, API keys, local models, routing groups) is served there
|
||||
under one id each, so your app needs one provider entry, not one per
|
||||
vendor.
|
||||
|
||||
**Find it.** The gateway listens on `127.0.0.1:3425`, or on the port in
|
||||
`port` of magpie's `settings.json` (`~/.config/magpie/settings.json`, the
|
||||
same path under the user's folder on Windows; `$XDG_CONFIG_HOME/magpie`
|
||||
when that is set). `GET /api/hello` answers
|
||||
`{"name":"magpie","version":"…"}` when it is magpie. Read that file, never
|
||||
write it. magpie must be running (the app or `magpie serve`). An app that
|
||||
finds nothing there should offer its own providers as usual.
|
||||
|
||||
**Endpoints.** Requests go straight through when the vendor speaks your
|
||||
API, and are translated otherwise, streaming, tool calls, images and
|
||||
reasoning included:
|
||||
|
||||
| Path | API |
|
||||
| ---- | --- |
|
||||
| `/v1/chat/completions` | OpenAI Chat Completions |
|
||||
| `/v1/responses` | OpenAI Responses |
|
||||
| `/v1/messages`, `/v1/messages/count_tokens` | Anthropic Messages |
|
||||
| `/v1beta/models/{model}:generateContent` (and `:streamGenerateContent`) | Google Gemini |
|
||||
| `/v1/models` | the catalog |
|
||||
|
||||
So the base URL is `http://127.0.0.1:<port>/v1` for an OpenAI client and
|
||||
`http://127.0.0.1:<port>` for an Anthropic or Gemini one.
|
||||
|
||||
**The key names your app.** On this machine the gateway takes any key.
|
||||
Send `magpie-<your-app-id>` (`Authorization: Bearer magpie-acme`, or
|
||||
`x-api-key`) and every call is counted as `acme` in Usage, the Requests
|
||||
log and the routing rules that match on agents. Without it, magpie counts
|
||||
the call by the first word of your `User-Agent` (`Acme/1.4 (darwin)` is
|
||||
`Acme`); an SDK's own User-Agent (`OpenAI/JS …`, `ai-sdk/…`) would count it
|
||||
as that SDK. From another computer (Settings → Share on local network) the
|
||||
key must be one of the user's gateway keys, and the User-Agent names your
|
||||
app.
|
||||
|
||||
**Models.** `GET /v1/models` lists what the user has turned on: `id`
|
||||
(`provider/model`, or a routing group's name, which you send as it is),
|
||||
`display_name`, `owned_by` (the provider), `reasoning` and
|
||||
`supported_reasoning_levels` (`[{"effort":"low"},…]`), `context_window`
|
||||
and `max_output_tokens` when known, `modalities` when magpie knows whether
|
||||
it takes images. List them from there rather than shipping a list: the
|
||||
user adds and removes providers in magpie. Reasoning is asked in your
|
||||
API's own field (`reasoning_effort`, `reasoning.effort`, Anthropic's
|
||||
`thinking` or `output_config.effort`). `?format=text` gives the ids one a
|
||||
line.
|
||||
|
||||
**Optional.** Send `X-Magpie-Session: <id>` with a conversation's requests
|
||||
and `GET /v1/magpie/route?session=<id>` says which model a routing group
|
||||
picked for the turn and the fallbacks it tried, before the first token
|
||||
(long-poll with `after=<seq>&wait=<s>`). `GET /v1/magpie/quotas` lists the
|
||||
user's subscription allowances. Both answer only this machine, or another
|
||||
with a gateway key. [Connecting anything else](reference.md#connecting-anything-else)
|
||||
has the details.
|
||||
|
||||
**Keep the user's providers.** Add magpie as one more provider (named
|
||||
Magpie) beside the ones the user set up in your app, rather than replacing
|
||||
them, so turning magpie off is picking another provider.
|
||||
|
||||
## 2. A row on the Agents page
|
||||
|
||||
The agents magpie lists (Claude Code, Codex, OpenCode, dsh, Alma, Cindy…)
|
||||
are adapters in [`internal/agent`](../internal/agent), one file each,
|
||||
listed in [`agents.go`](../internal/agent/agents.go). A row gets your
|
||||
app's name and icon, shows when your app is installed, lets the user pick
|
||||
its model (and reasoning) from magpie's catalog, follows the catalog as
|
||||
providers change, and on disconnect puts back what your app had before.
|
||||
There is no runtime registration: a row is added by a pull request to
|
||||
this repository, and ships in the next release.
|
||||
|
||||
An adapter is an [`Agent`](../internal/agent/agent.go) value. What your
|
||||
app offers decides its shape:
|
||||
|
||||
- **magpie edits your config file** (most adapters; [`fx.go`](../internal/agent/fx.go),
|
||||
[`empryo.go`](../internal/agent/empryo.go)). `Dir`, `Path` and `Bin` find
|
||||
the app; each `Field` (`model`, `effort`, …) has `Get`, `Set` and
|
||||
`Options`. Connecting adds a `magpie` provider at the gateway, keyed
|
||||
`magpie-<id>`, and sets your model to one of magpie's; `Set("")` (the
|
||||
agent's default) or `Unwire` takes magpie's entries out and restores
|
||||
what was there. Only the keys magpie sets are touched; the user's other
|
||||
settings, providers and comments stay. `Notice` says when your app must
|
||||
be restarted to see a change; `Sync` rewrites a model list your app keeps
|
||||
in a file rather than asking `/v1/models`.
|
||||
- **Your app takes a provider only through its own import link, which the
|
||||
user confirms in your app** ([`cindy.go`](../internal/agent/cindy.go)).
|
||||
`Import` returns the link (`yourapp://provider/import?…` carrying the
|
||||
gateway's URL, the key `magpie-<id>` and the endpoints), the row's button
|
||||
opens it, and the user says yes once, in your app. `Added` reads,
|
||||
read-only, whether your app has magpie already. magpie writes nothing of
|
||||
yours. This suits an app that keeps its providers in a database or
|
||||
encrypted.
|
||||
- **Your app has a local API while it runs** (Alma, at `localhost:23001`):
|
||||
magpie adds itself as a provider and sets the default model through it.
|
||||
|
||||
A pull request needs: the adapter and its registration, `UA` (what your
|
||||
User-Agent begins with, lower-case) so Usage knows your calls, an icon in
|
||||
`internal/gui/assets/icons`, tests that run under a temporary `HOME`
|
||||
(connect, switch, disconnect restoring the original file), and a row in
|
||||
the [Agents table](reference.md#agents). What helps us most is a stable,
|
||||
documented config format or import link, and where your app keeps it on
|
||||
each system.
|
||||
|
||||
## 3. Plugins
|
||||
|
||||
A plugin is an npm package magpie runs; the user installs it from
|
||||
Settings → Plugins or with `magpie plugin add <package>`. There are two
|
||||
kinds ([plugin guide](https://usemagpie.ai/docs/plugins)):
|
||||
|
||||
- **Provider plugins**, OpenCode's `auth` hook or a pi package: a new
|
||||
source of models, such as a subscription magpie doesn't sign in to
|
||||
itself. They sign in, list models and make requests.
|
||||
- **Gateway middleware**: rewrites requests and replies as they pass
|
||||
(`onRequest`, `onEvent`, `onResponse`).
|
||||
|
||||
A plugin can't add a row to the Agents page or write another app's
|
||||
config, so it is not how an app registers itself.
|
||||
|
||||
## What isn't there
|
||||
|
||||
- No API or link lets another app register itself as an agent, or install
|
||||
a plugin, without the user: plugins are installed in magpie by the user,
|
||||
and agent rows come from adapters in magpie's source.
|
||||
- `magpie://import` links add a *provider* (a source of models, with its
|
||||
key), not an agent or an app; see [Import links](reference.md#import-links).
|
||||
|
||||
Questions, or an adapter you'd like to discuss first: open an issue at
|
||||
<https://github.com/yetone/magpie/issues> or ask on
|
||||
[Discord](https://discord.gg/vGSnD3ZKQF).
|
||||
@@ -708,6 +708,9 @@ deployments, set `MAGPIE_PUBLIC_URL=https://magpie.example.com` to the base
|
||||
URL shown in the console and CLI, including connection examples. Local
|
||||
agent configs still use the local gateway address.
|
||||
|
||||
Building an app or agent that should use magpie, or get a row on the
|
||||
Agents page: see [Integrating your app or agent](integrating.md).
|
||||
|
||||
A reverse proxy must enforce authentication itself, or you must enable
|
||||
Settings → Share on local network and use an enabled gateway key
|
||||
(Gateway → Gateway keys) for external clients. A public URL with no port of
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/yetone/magpie/internal/provider"
|
||||
"github.com/yetone/magpie/internal/usage"
|
||||
)
|
||||
|
||||
// What docs/integrating.md promises an app magpie has no adapter for (#918):
|
||||
// /api/hello finds the gateway, and its calls are counted as its own in
|
||||
// Usage, by the key it sends (magpie-<app>) or else by its User-Agent.
|
||||
func TestThirdPartyAppIsCountedAsItself(t *testing.T) {
|
||||
setHome(t, t.TempDir())
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
New().Handler().ServeHTTP(rec, httptest.NewRequest("GET", "/api/hello", nil))
|
||||
var hello struct{ Name string }
|
||||
if rec.Code != 200 || json.Unmarshal(rec.Body.Bytes(), &hello) != nil || hello.Name != "magpie" {
|
||||
t.Fatalf("GET /api/hello: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
f := &fake{t: t, reply: sse(
|
||||
`data: {"id":"c1","choices":[{"delta":{"content":"OK"},"finish_reason":"stop"}],"usage":{"prompt_tokens":5,"completion_tokens":2}}`,
|
||||
`data: [DONE]`)}
|
||||
setup(t, provider.Chat, f)
|
||||
body := `{"model":"fake/m1","stream":true,"messages":[{"role":"user","content":"hi"}]}`
|
||||
for i, c := range []struct{ key, ua, want string }{
|
||||
{TokenFor("my-app"), "node-fetch/1.0", "my-app"},
|
||||
{Token, "MyApp/1.2 (darwin)", "MyApp"},
|
||||
} {
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/v1/chat/completions", strings.NewReader(body))
|
||||
req.Header.Set("Authorization", "Bearer "+c.key)
|
||||
req.Header.Set("User-Agent", c.ua)
|
||||
New().Handler().ServeHTTP(rec, req)
|
||||
if rec.Code != 200 || !strings.Contains(rec.Body.String(), "OK") {
|
||||
t.Fatalf("%+v: %d %s", c, rec.Code, rec.Body.String())
|
||||
}
|
||||
var got []usage.Record
|
||||
for range 100 {
|
||||
if got = usage.Load(time.Time{}); len(got) > i {
|
||||
break
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
if len(got) <= i || got[i].Agent != c.want {
|
||||
t.Fatalf("%+v: records %+v, want the last %s's", c, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Integrate your app · magpie docs</title>
|
||||
<meta name="description" content="How an AI app, agent or client works with magpie: the local gateway, its endpoints and models, being counted as your app, a row on the Agents page, and plugins.">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="theme-color" content="#fafafa">
|
||||
<script>
|
||||
(() => {
|
||||
let mode = "system";
|
||||
try { mode = localStorage.getItem("theme") || "system"; } catch {}
|
||||
const q = matchMedia("(prefers-color-scheme: dark)");
|
||||
const apply = () => {
|
||||
const t = mode === "system" ? (q.matches ? "dark" : "light") : mode;
|
||||
document.documentElement.dataset.theme = t;
|
||||
document.querySelector('meta[name="theme-color"]').content = t === "dark" ? "#0b0b0c" : "#fafafa";
|
||||
document.querySelectorAll("button.theme").forEach((b) => { b.dataset.mode = mode; b.title = "Theme: " + mode; });
|
||||
};
|
||||
q.addEventListener("change", () => mode === "system" && apply());
|
||||
window.setTheme = (m) => { mode = m; try { m === "system" ? localStorage.removeItem("theme") : localStorage.setItem("theme", m); } catch {} apply(); };
|
||||
window.themeMode = () => mode;
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
<script>try{if(/Linux/.test(navigator.userAgent)&&!/Android/.test(navigator.userAgent)||localStorage.getItem("omarchy")||/[?&]omarchy=/.test(location.search))document.write('<script src="/omarchy.js"><\/script>')}catch(e){}</script>
|
||||
<link rel="icon" href="/favicon.png" type="image/png">
|
||||
<link rel="apple-touch-icon" href="/img/icon-256.png">
|
||||
<link rel="alternate" hreflang="en" href="https://usemagpie.ai/docs/integrate">
|
||||
<link rel="alternate" hreflang="zh" href="https://usemagpie.ai/docs/zh/integrate">
|
||||
<link rel="stylesheet" href="/docs.css">
|
||||
<meta property="og:title" content="Integrate your app">
|
||||
<meta property="og:description" content="Use magpie's gateway from your app, or get a row on its Agents page.">
|
||||
<meta property="og:image" content="https://usemagpie.ai/img/og.png">
|
||||
<meta property="og:url" content="https://usemagpie.ai/docs/integrate">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav><div class="wrap">
|
||||
<a class="brand" href="/"><img class="bird" src="/img/magpie-small.svg" alt="">magpie <small>Docs</small></a>
|
||||
<div class="links">
|
||||
<a href="/">Home</a>
|
||||
<a href="/docs/start">Get started</a>
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
<button class="icon-btn lang" type="button" aria-label="Language" title="Language" aria-haspopup="true" aria-expanded="false" aria-controls="langs"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M1.75 8h12.5M8 1.75c1.7 1.75 2.55 3.83 2.55 6.25S9.7 12.5 8 14.25M8 1.75C6.3 3.5 5.45 5.58 5.45 8S6.3 12.5 8 14.25"/></svg></button>
|
||||
<div class="lang-menu" id="langs" hidden>
|
||||
<a href="/docs/integrate" hreflang="en" lang="en">English</a>
|
||||
<a href="/docs/zh/integrate" hreflang="zh" lang="zh">中文</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="icon-btn" href="https://discord.gg/vGSnD3ZKQF" aria-label="magpie on Discord" title="Join us on Discord"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg></a>
|
||||
<a class="icon-btn" href="https://github.com/yetone/magpie" aria-label="magpie on GitHub" title="Source on GitHub"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg></a>
|
||||
<button class="icon-btn theme" type="button" aria-label="Theme" title="Theme: system"><svg class="i-system" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1.75" y="2.75" width="12.5" height="8.5" rx="1.5"/><path d="M5.5 14h5M8 11.25V14"/></svg><svg class="i-light" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="3"/><path d="M8 1v1.5M8 13.5V15M1 8h1.5M13.5 8H15M3 3l1 1M12 12l1 1M3 13l1-1M12 4l1-1"/></svg><svg class="i-dark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"><path d="M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7Z"/></svg></button>
|
||||
<a class="btn small" href="/#get">Download</a>
|
||||
</div></nav>
|
||||
|
||||
<main class="wrap doc">
|
||||
<aside class="toc">
|
||||
<p>Integrate an app</p>
|
||||
<a href="#overview">Overview</a>
|
||||
<a href="#gateway">Talk to the gateway</a>
|
||||
<a href="#agent">A row on the Agents page</a>
|
||||
<a href="#plugins">Plugins</a>
|
||||
<a href="#not">What isn’t there</a>
|
||||
</aside>
|
||||
|
||||
<article>
|
||||
<p class="eyebrow" id="overview">Docs · For developers</p>
|
||||
<h1>Integrate your app</h1>
|
||||
<p class="lede">For developers of an AI app, agent or client who want it to work well with magpie. There are three levels, from no work on magpie’s side to a row of your own on the Agents page.</p>
|
||||
|
||||
<div class="table-wrap"><table>
|
||||
<tr><th>You want</th><th>Route</th><th>Change to magpie</th></tr>
|
||||
<tr><td>Your app’s requests served by magpie, counted under its own name in Usage</td><td><a href="#gateway">Talk to the gateway</a></td><td>none</td></tr>
|
||||
<tr><td>A row on the Agents page: detected, its model picked there, put back on disconnect</td><td><a href="#agent">An agent adapter</a></td><td>a pull request</td></tr>
|
||||
<tr><td>A new model source (a subscription, a vendor), or rewriting requests</td><td><a href="#plugins">A plugin</a></td><td>none</td></tr>
|
||||
</table></div>
|
||||
|
||||
<h2 id="gateway"><a class="anchor" href="#gateway">1. Talk to the gateway</a></h2>
|
||||
<p>magpie runs a local LLM gateway. Every model the user has in magpie (subscriptions, API keys, local models, routing groups) is served there under one id each, so your app needs one provider entry, not one per vendor.</p>
|
||||
|
||||
<h3>Find it</h3>
|
||||
<p>The gateway listens on <code>127.0.0.1:3425</code>, or on the port in <code>port</code> of magpie’s <code>settings.json</code> (<code>~/.config/magpie/settings.json</code>, the same path under the user’s folder on Windows; <code>$XDG_CONFIG_HOME/magpie</code> when that is set). <code>GET /api/hello</code> answers <code>{"name":"magpie","version":"…"}</code> when it is magpie. Read that file, never write it. magpie must be running (the app or <code>magpie serve</code>); an app that finds nothing there offers its own providers as usual.</p>
|
||||
<div class="code">
|
||||
<span class="lang">Shell</span>
|
||||
<button class="copy" data-copy="ex-hello">Copy</button>
|
||||
<pre id="ex-hello">curl -s http://127.0.0.1:3425/api/hello
|
||||
curl -s http://127.0.0.1:3425/v1/models -H "Authorization: Bearer magpie-acme"</pre>
|
||||
</div>
|
||||
|
||||
<h3>Endpoints</h3>
|
||||
<p>Requests go straight through when the vendor speaks your API, and are translated otherwise, streaming, tool calls, images and reasoning included. The base URL is <code>http://127.0.0.1:<port>/v1</code> for an OpenAI client and <code>http://127.0.0.1:<port></code> for an Anthropic or Gemini one.</p>
|
||||
<div class="table-wrap"><table>
|
||||
<tr><th>Path</th><th>API</th></tr>
|
||||
<tr><td><code>/v1/chat/completions</code></td><td>OpenAI Chat Completions</td></tr>
|
||||
<tr><td><code>/v1/responses</code></td><td>OpenAI Responses</td></tr>
|
||||
<tr><td><code>/v1/messages</code>, <code>/v1/messages/count_tokens</code></td><td>Anthropic Messages</td></tr>
|
||||
<tr><td><code>/v1beta/models/{model}:generateContent</code> (and <code>:streamGenerateContent</code>)</td><td>Google Gemini</td></tr>
|
||||
<tr><td><code>/v1/models</code></td><td>the catalog</td></tr>
|
||||
</table></div>
|
||||
|
||||
<h3>The key names your app</h3>
|
||||
<p>On this machine the gateway takes any key. Send <code>magpie-<your-app-id></code> (<code>Authorization: Bearer magpie-acme</code>, or <code>x-api-key</code>) and every call is counted as <code>acme</code> in Usage, the Requests log and the routing rules that match on agents. Without it, magpie counts the call by the first word of your <code>User-Agent</code> (<code>Acme/1.4 (darwin)</code> is <code>Acme</code>); an SDK’s own User-Agent (<code>OpenAI/JS …</code>, <code>ai-sdk/…</code>) would count it as that SDK. From another computer (<span class="ui">Settings</span> → <span class="ui">Share on local network</span>) the key must be one of the user’s gateway keys, and the User-Agent names your app.</p>
|
||||
|
||||
<h3>Models</h3>
|
||||
<p><code>GET /v1/models</code> lists what the user has turned on: <code>id</code> (<code>provider/model</code>, or a routing group’s name, sent as it is), <code>display_name</code>, <code>owned_by</code> (the provider), <code>reasoning</code> and <code>supported_reasoning_levels</code> (<code>[{"effort":"low"},…]</code>), <code>context_window</code> and <code>max_output_tokens</code> when known, and <code>modalities</code> when magpie knows whether it takes images. List them from there rather than shipping a list: the user adds and removes providers in magpie. Reasoning is asked in your API’s own field (<code>reasoning_effort</code>, <code>reasoning.effort</code>, Anthropic’s <code>thinking</code> or <code>output_config.effort</code>). <code>?format=text</code> gives the ids one a line.</p>
|
||||
|
||||
<h3>Optional</h3>
|
||||
<p>Send <code>X-Magpie-Session: <id></code> with a conversation’s requests and <code>GET /v1/magpie/route?session=<id></code> says which model a routing group picked for the turn and the fallbacks it tried, before the first token (long-poll with <code>after=<seq>&wait=<s></code>). <code>GET /v1/magpie/quotas</code> lists the user’s subscription allowances. Both answer only this machine, or another with a gateway key. The <a href="https://github.com/yetone/magpie/blob/main/docs/reference.md#connecting-anything-else">reference</a> has the details.</p>
|
||||
|
||||
<div class="callout"><b>Keep the user’s providers.</b> Add magpie as one more provider (named Magpie) beside the ones the user set up in your app, rather than replacing them, so turning magpie off is picking another provider.</div>
|
||||
|
||||
<h2 id="agent"><a class="anchor" href="#agent">2. A row on the Agents page</a></h2>
|
||||
<p>The agents magpie lists (Claude Code, Codex, OpenCode, dsh, Alma, Cindy…) are adapters in <a href="https://github.com/yetone/magpie/tree/main/internal/agent"><code>internal/agent</code></a>, one file each. A row gets your app’s name and icon, shows when your app is installed, lets the user pick its model (and reasoning) from magpie’s catalog, follows the catalog as providers change, and on disconnect puts back what your app had before. There is no runtime registration: a row is added by a pull request to magpie, and ships in the next release.</p>
|
||||
<p>An adapter is an <a href="https://github.com/yetone/magpie/blob/main/internal/agent/agent.go"><code>Agent</code></a> value. What your app offers decides its shape:</p>
|
||||
<ul>
|
||||
<li><b>magpie edits your config file</b> (most adapters; <a href="https://github.com/yetone/magpie/blob/main/internal/agent/fx.go"><code>fx.go</code></a>, <a href="https://github.com/yetone/magpie/blob/main/internal/agent/empryo.go"><code>empryo.go</code></a>). <code>Dir</code>, <code>Path</code> and <code>Bin</code> find the app; each <code>Field</code> (<code>model</code>, <code>effort</code>…) has <code>Get</code>, <code>Set</code> and <code>Options</code>. Connecting adds a <code>magpie</code> provider at the gateway, keyed <code>magpie-<id></code>, and sets your model to one of magpie’s; the agent’s default or <code>Unwire</code> takes magpie’s entries out and restores what was there. Only the keys magpie sets are touched. <code>Notice</code> says when your app must be restarted; <code>Sync</code> rewrites a model list your app keeps in a file.</li>
|
||||
<li><b>Your app takes a provider only through its own import link, confirmed in your app</b> (<a href="https://github.com/yetone/magpie/blob/main/internal/agent/cindy.go"><code>cindy.go</code></a>). <code>Import</code> returns the link (<code>yourapp://provider/import?…</code> with the gateway’s URL, the key <code>magpie-<id></code> and the endpoints), the row’s button opens it, and the user says yes once, in your app. <code>Added</code> reads, read-only, whether your app has magpie already. magpie writes nothing of yours. This suits an app that keeps its providers in a database or encrypted.</li>
|
||||
<li><b>Your app has a local API while it runs</b> (Alma): magpie adds itself as a provider and sets the default model through it.</li>
|
||||
</ul>
|
||||
<p>A pull request needs the adapter and its entry in <code>agents.go</code>, <code>UA</code> (what your User-Agent begins with, lower-case) so Usage knows your calls, an icon in <code>internal/gui/assets/icons</code>, tests that run under a temporary <code>HOME</code> (connect, switch, disconnect restoring the original file), and a row in the reference’s Agents table. What helps most is a stable, documented config format or import link, and where your app keeps it on each system.</p>
|
||||
|
||||
<h2 id="plugins"><a class="anchor" href="#plugins">3. Plugins</a></h2>
|
||||
<p>A plugin is an npm package magpie runs; the user installs it from <span class="ui">Settings</span> → <span class="ui">Plugins</span> or with <code>magpie plugin add <package></code>. <b>Provider plugins</b> (OpenCode’s <code>auth</code> hook, or a pi package) add a source of models, such as a subscription magpie doesn’t sign in to itself. <b>Gateway middleware</b> rewrites requests and replies as they pass. See the <a href="/docs/plugins">plugin guide</a>. A plugin can’t add a row to the Agents page or write another app’s config, so it is not how an app registers itself.</p>
|
||||
|
||||
<h2 id="not"><a class="anchor" href="#not">What isn’t there</a></h2>
|
||||
<ul>
|
||||
<li>No API or link lets another app register itself as an agent, or install a plugin, without the user: plugins are installed in magpie by the user, and agent rows come from adapters in magpie’s source.</li>
|
||||
<li><code>magpie://import</code> links add a <em>provider</em> (a source of models, with its key), not an agent or an app: see <a href="/docs/import">Add to magpie</a>.</li>
|
||||
</ul>
|
||||
|
||||
<p style="margin-top:48px">Questions, or an adapter you’d like to discuss first: open an <a href="https://github.com/yetone/magpie/issues">issue</a> or ask on <a href="https://discord.gg/vGSnD3ZKQF">Discord</a>.</p>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<footer><div class="wrap">
|
||||
<span>© 2026 magpie · MIT</span>
|
||||
<div class="links">
|
||||
<a href="/">Home</a>
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
</div>
|
||||
</div></footer>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const next = { system: "light", light: "dark", dark: "system" };
|
||||
document.querySelectorAll("button.theme").forEach((b) => {
|
||||
b.dataset.mode = themeMode();
|
||||
b.title = "Theme: " + themeMode();
|
||||
b.addEventListener("click", () => setTheme(next[themeMode()]));
|
||||
});
|
||||
|
||||
document.querySelectorAll(".copy").forEach((b) => b.addEventListener("click", async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(document.getElementById(b.dataset.copy).textContent);
|
||||
b.textContent = "Copied"; setTimeout(() => (b.textContent = "Copy"), 1400);
|
||||
} catch {}
|
||||
}));
|
||||
})();
|
||||
</script>
|
||||
<script src="/docs.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -316,6 +317,7 @@ The number of its kind (0 for none):</pre>
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/ja/intent">インテントルーティング</a>
|
||||
<a href="/docs/ja/plugins">プラグイン</a>
|
||||
<a href="/docs/ja/import">magpie に追加</a>
|
||||
<a href="/docs/integrate">アプリ連携</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/ja/intent">インテントルーティング</a>
|
||||
<a href="/docs/ja/plugins">プラグイン</a>
|
||||
<a href="/docs/ja/import">magpie に追加</a>
|
||||
<a href="/docs/integrate">アプリ連携</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -316,6 +317,7 @@ The number of its kind (0 for none):</pre>
|
||||
<a href="/docs/ja/intent">インテントルーティング</a>
|
||||
<a href="/docs/ja/plugins">プラグイン</a>
|
||||
<a href="/docs/ja/import">magpie に追加</a>
|
||||
<a href="/docs/integrate">アプリ連携</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/ja/intent">インテントルーティング</a>
|
||||
<a href="/docs/ja/plugins">プラグイン</a>
|
||||
<a href="/docs/ja/import">magpie に追加</a>
|
||||
<a href="/docs/integrate">アプリ連携</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -707,6 +708,7 @@ magpie plugin options model-map '{"mapping": {"fast": "deepseek/deepseek-chat"}}
|
||||
<a href="/docs/ja/intent">インテントルーティング</a>
|
||||
<a href="/docs/ja/plugins">プラグイン</a>
|
||||
<a href="/docs/ja/import">magpie に追加</a>
|
||||
<a href="/docs/integrate">アプリ連携</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/ja/intent">インテントルーティング</a>
|
||||
<a href="/docs/ja/plugins">プラグイン</a>
|
||||
<a href="/docs/ja/import">magpie に追加</a>
|
||||
<a href="/docs/integrate">アプリ連携</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -271,6 +272,7 @@ magpie claude group/opus-anywhere <span class="c"># これを使う</span></p
|
||||
<a href="/docs/ja/intent">インテントルーティング</a>
|
||||
<a href="/docs/ja/plugins">プラグイン</a>
|
||||
<a href="/docs/ja/import">magpie に追加</a>
|
||||
<a href="/docs/integrate">アプリ連携</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">リリース</a>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -707,6 +708,7 @@ magpie plugin options model-map '{"mapping": {"fast": "deepseek/deepseek-chat"}}
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -271,6 +272,7 @@ magpie claude group/opus-anywhere <span class="c"># use it</span></pre>
|
||||
<a href="/docs/intent">Intent routing</a>
|
||||
<a href="/docs/plugins">Plugins</a>
|
||||
<a href="/docs/import">Add to magpie</a>
|
||||
<a href="/docs/integrate">Integrate</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">Releases</a>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>接入你的应用 · magpie 文档</title>
|
||||
<meta name="description" content="AI 应用、Agent 或客户端如何与 magpie 配合:本地网关、端点与模型、以应用自己的名字统计用量、在 Agents 页面占一行,以及插件。">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="theme-color" content="#fafafa">
|
||||
<script>
|
||||
(() => {
|
||||
let mode = "system";
|
||||
try { mode = localStorage.getItem("theme") || "system"; } catch {}
|
||||
const q = matchMedia("(prefers-color-scheme: dark)");
|
||||
const apply = () => {
|
||||
const t = mode === "system" ? (q.matches ? "dark" : "light") : mode;
|
||||
document.documentElement.dataset.theme = t;
|
||||
document.querySelector('meta[name="theme-color"]').content = t === "dark" ? "#0b0b0c" : "#fafafa";
|
||||
document.querySelectorAll("button.theme").forEach((b) => { b.dataset.mode = mode; b.title = "Theme: " + mode; });
|
||||
};
|
||||
q.addEventListener("change", () => mode === "system" && apply());
|
||||
window.setTheme = (m) => { mode = m; try { m === "system" ? localStorage.removeItem("theme") : localStorage.setItem("theme", m); } catch {} apply(); };
|
||||
window.themeMode = () => mode;
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
<script>try{if(/Linux/.test(navigator.userAgent)&&!/Android/.test(navigator.userAgent)||localStorage.getItem("omarchy")||/[?&]omarchy=/.test(location.search))document.write('<script src="/omarchy.js"><\/script>')}catch(e){}</script>
|
||||
<link rel="icon" href="/favicon.png" type="image/png">
|
||||
<link rel="apple-touch-icon" href="/img/icon-256.png">
|
||||
<link rel="alternate" hreflang="en" href="https://usemagpie.ai/docs/integrate">
|
||||
<link rel="alternate" hreflang="zh" href="https://usemagpie.ai/docs/zh/integrate">
|
||||
<link rel="stylesheet" href="/docs.css">
|
||||
<meta property="og:title" content="接入你的应用">
|
||||
<meta property="og:description" content="在你的应用里使用 magpie 的网关,或在 Agents 页面拥有自己的一行。">
|
||||
<meta property="og:image" content="https://usemagpie.ai/img/og.png">
|
||||
<meta property="og:url" content="https://usemagpie.ai/docs/zh/integrate">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav><div class="wrap">
|
||||
<a class="brand" href="/zh/"><img class="bird" src="/img/magpie-small.svg" alt="">magpie <small>文档</small></a>
|
||||
<div class="links">
|
||||
<a href="/zh/">首页</a>
|
||||
<a href="/docs/zh/start">快速上手</a>
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
<button class="icon-btn lang" type="button" aria-label="语言" title="语言" aria-haspopup="true" aria-expanded="false" aria-controls="langs"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M1.75 8h12.5M8 1.75c1.7 1.75 2.55 3.83 2.55 6.25S9.7 12.5 8 14.25M8 1.75C6.3 3.5 5.45 5.58 5.45 8S6.3 12.5 8 14.25"/></svg></button>
|
||||
<div class="lang-menu" id="langs" hidden>
|
||||
<a href="/docs/integrate" hreflang="en" lang="en">English</a>
|
||||
<a href="/docs/zh/integrate" hreflang="zh" lang="zh">中文</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="icon-btn" href="https://discord.gg/vGSnD3ZKQF" aria-label="magpie on Discord" title="Join us on Discord"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg></a>
|
||||
<a class="icon-btn" href="https://github.com/yetone/magpie" aria-label="magpie on GitHub" title="Source on GitHub"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg></a>
|
||||
<button class="icon-btn theme" type="button" aria-label="Theme" title="Theme: system"><svg class="i-system" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1.75" y="2.75" width="12.5" height="8.5" rx="1.5"/><path d="M5.5 14h5M8 11.25V14"/></svg><svg class="i-light" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="3"/><path d="M8 1v1.5M8 13.5V15M1 8h1.5M13.5 8H15M3 3l1 1M12 12l1 1M3 13l1-1M12 4l1-1"/></svg><svg class="i-dark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"><path d="M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7Z"/></svg></button>
|
||||
<a class="btn small" href="/#get">下载</a>
|
||||
</div></nav>
|
||||
|
||||
<main class="wrap doc">
|
||||
<aside class="toc">
|
||||
<p>接入你的应用</p>
|
||||
<a href="#overview">概览</a>
|
||||
<a href="#gateway">直接调用网关</a>
|
||||
<a href="#agent">在 Agents 页面占一行</a>
|
||||
<a href="#plugins">插件</a>
|
||||
<a href="#not">目前没有的</a>
|
||||
</aside>
|
||||
|
||||
<article>
|
||||
<p class="eyebrow" id="overview">文档 · 开发者</p>
|
||||
<h1>接入你的应用</h1>
|
||||
<p class="lede">写给 AI 应用、Agent 或客户端的开发者:想让自己的应用和 magpie 配合好,有三个层次,从 magpie 这边什么都不用改,到在 Agents 页面拥有自己的一行。</p>
|
||||
|
||||
<div class="table-wrap"><table>
|
||||
<tr><th>你想要</th><th>做法</th><th>magpie 需要改动</th></tr>
|
||||
<tr><td>应用的请求由 magpie 提供服务,并在用量里以自己的名字统计</td><td><a href="#gateway">直接调用网关</a></td><td>无</td></tr>
|
||||
<tr><td>Agents 页面上的一行:自动检测、在那里选模型、断开时还原</td><td><a href="#agent">Agent 适配器</a></td><td>一个 PR</td></tr>
|
||||
<tr><td>新的模型来源(订阅、厂商),或改写请求</td><td><a href="#plugins">插件</a></td><td>无</td></tr>
|
||||
</table></div>
|
||||
|
||||
<h2 id="gateway"><a class="anchor" href="#gateway">1. 直接调用网关</a></h2>
|
||||
<p>magpie 在本机运行一个 LLM 网关。用户在 magpie 里有的所有模型(订阅、API key、本地模型、路由组)都以各自的 id 在那里提供,所以你的应用只需要一个供应商条目,而不是每个厂商一个。</p>
|
||||
|
||||
<h3>找到它</h3>
|
||||
<p>网关监听 <code>127.0.0.1:3425</code>,或 magpie 的 <code>settings.json</code> 里 <code>port</code> 指定的端口(<code>~/.config/magpie/settings.json</code>,Windows 上是用户目录下的同一路径;设置了 <code>$XDG_CONFIG_HOME</code> 时为 <code>$XDG_CONFIG_HOME/magpie</code>)。<code>GET /api/hello</code> 在是 magpie 时返回 <code>{"name":"magpie","version":"…"}</code>。这个文件只读、不要写。magpie 需要在运行(应用本身或 <code>magpie serve</code>);找不到时,你的应用照常提供它自己的供应商即可。</p>
|
||||
<div class="code">
|
||||
<span class="lang">Shell</span>
|
||||
<button class="copy" data-copy="ex-hello">Copy</button>
|
||||
<pre id="ex-hello">curl -s http://127.0.0.1:3425/api/hello
|
||||
curl -s http://127.0.0.1:3425/v1/models -H "Authorization: Bearer magpie-acme"</pre>
|
||||
</div>
|
||||
|
||||
<h3>端点</h3>
|
||||
<p>厂商本身支持你的 API 时请求原样转发,否则会被转换,流式、工具调用、图片和推理都包括在内。OpenAI 客户端的 base URL 是 <code>http://127.0.0.1:<port>/v1</code>,Anthropic 或 Gemini 客户端是 <code>http://127.0.0.1:<port></code>。</p>
|
||||
<div class="table-wrap"><table>
|
||||
<tr><th>路径</th><th>API</th></tr>
|
||||
<tr><td><code>/v1/chat/completions</code></td><td>OpenAI Chat Completions</td></tr>
|
||||
<tr><td><code>/v1/responses</code></td><td>OpenAI Responses</td></tr>
|
||||
<tr><td><code>/v1/messages</code>、<code>/v1/messages/count_tokens</code></td><td>Anthropic Messages</td></tr>
|
||||
<tr><td><code>/v1beta/models/{model}:generateContent</code>(以及 <code>:streamGenerateContent</code>)</td><td>Google Gemini</td></tr>
|
||||
<tr><td><code>/v1/models</code></td><td>模型目录</td></tr>
|
||||
</table></div>
|
||||
|
||||
<h3>用 key 标明你的应用</h3>
|
||||
<p>在本机,网关接受任意 key。发送 <code>magpie-<你的应用 id></code>(<code>Authorization: Bearer magpie-acme</code>,或 <code>x-api-key</code>),每次调用都会在用量、请求记录以及按 Agent 匹配的路由规则里记为 <code>acme</code>。不带它时,magpie 按 <code>User-Agent</code> 的第一个词统计(<code>Acme/1.4 (darwin)</code> 记为 <code>Acme</code>);SDK 自带的 User-Agent(<code>OpenAI/JS …</code>、<code>ai-sdk/…</code>)会被记成那个 SDK。从另一台电脑访问时(<span class="ui">设置</span> → <span class="ui">局域网共享</span>),key 必须是用户的网关密钥之一,应用名就靠 User-Agent。</p>
|
||||
|
||||
<h3>模型</h3>
|
||||
<p><code>GET /v1/models</code> 列出用户打开的模型:<code>id</code>(<code>provider/model</code>,或路由组的名字,原样发送即可)、<code>display_name</code>、<code>owned_by</code>(供应商)、<code>reasoning</code> 与 <code>supported_reasoning_levels</code>(<code>[{"effort":"low"},…]</code>),已知时还有 <code>context_window</code>、<code>max_output_tokens</code>,以及 magpie 知道是否支持图片时的 <code>modalities</code>。请从这里读取,而不是内置一份列表:用户会在 magpie 里增删供应商。推理强度用你所用 API 自己的字段传(<code>reasoning_effort</code>、<code>reasoning.effort</code>、Anthropic 的 <code>thinking</code> 或 <code>output_config.effort</code>)。<code>?format=text</code> 每行一个 id。</p>
|
||||
|
||||
<h3>可选</h3>
|
||||
<p>在同一会话的请求里带上 <code>X-Magpie-Session: <id></code>,<code>GET /v1/magpie/route?session=<id></code> 就能在第一个 token 之前告诉你路由组为这一轮选了哪个模型、尝试过哪些后备(用 <code>after=<seq>&wait=<s></code> 长轮询)。<code>GET /v1/magpie/quotas</code> 列出用户各订阅的额度。两者只回答本机,或带网关密钥的其他机器。细节见<a href="https://github.com/yetone/magpie/blob/main/docs/reference.md#connecting-anything-else">参考文档</a>。</p>
|
||||
|
||||
<div class="callout"><b>保留用户自己的供应商。</b>把 magpie 作为又一个供应商(名为 Magpie)加在用户已有的供应商旁边,而不是替换它们,这样关掉 magpie 就只是换选另一个供应商。</div>
|
||||
|
||||
<h2 id="agent"><a class="anchor" href="#agent">2. 在 Agents 页面占一行</a></h2>
|
||||
<p>magpie 列出的 Agent(Claude Code、Codex、OpenCode、dsh、Alma、Cindy……)都是 <a href="https://github.com/yetone/magpie/tree/main/internal/agent"><code>internal/agent</code></a> 里的适配器,每个一个文件。有了这一行,你的应用会显示自己的名字和图标,安装后自动出现,用户可以从 magpie 的目录里为它选模型(和推理强度),供应商变化时跟着更新,断开时还原你的应用原来的设置。目前没有运行时注册:这一行通过给 magpie 提 PR 加入,随下一个版本发布。</p>
|
||||
<p>适配器是一个 <a href="https://github.com/yetone/magpie/blob/main/internal/agent/agent.go"><code>Agent</code></a> 值,形态取决于你的应用提供什么:</p>
|
||||
<ul>
|
||||
<li><b>magpie 编辑你的配置文件</b>(大多数适配器,如 <a href="https://github.com/yetone/magpie/blob/main/internal/agent/fx.go"><code>fx.go</code></a>、<a href="https://github.com/yetone/magpie/blob/main/internal/agent/empryo.go"><code>empryo.go</code></a>)。<code>Dir</code>、<code>Path</code>、<code>Bin</code> 用来检测应用;每个 <code>Field</code>(<code>model</code>、<code>effort</code>……)有 <code>Get</code>、<code>Set</code>、<code>Options</code>。连接时加入一个指向网关、key 为 <code>magpie-<id></code> 的 <code>magpie</code> 供应商,并把模型设为 magpie 的模型;选回默认或 <code>Unwire</code> 会移除 magpie 的条目并还原原值。只动 magpie 设置的那些键。<code>Notice</code> 提示何时需要重启应用;<code>Sync</code> 更新你的应用保存在文件里的模型列表。</li>
|
||||
<li><b>你的应用只通过自己的导入链接接收供应商,由用户在你的应用里确认</b>(<a href="https://github.com/yetone/magpie/blob/main/internal/agent/cindy.go"><code>cindy.go</code></a>)。<code>Import</code> 返回这个链接(<code>yourapp://provider/import?…</code>,带网关地址、key <code>magpie-<id></code> 和各端点),这一行的按钮会打开它,用户在你的应用里确认一次即可。<code>Added</code> 只读地检查你的应用是否已经有 magpie。magpie 不写你的任何文件。适合把供应商存在数据库里或加密保存的应用——这也最接近“检测到 magpie → 用户确认一次 → 完成接入”。</li>
|
||||
<li><b>你的应用运行时有本地 API</b>(如 Alma):magpie 通过它把自己加为供应商并设置默认模型。</li>
|
||||
</ul>
|
||||
<p>一个 PR 需要:适配器及其在 <code>agents.go</code> 里的注册;<code>UA</code>(你的 User-Agent 开头,小写),让用量认得你的调用;<code>internal/gui/assets/icons</code> 里的图标;在临时 <code>HOME</code> 下运行的测试(连接、切换、断开后原文件还原);参考文档 Agents 表里的一行。最有帮助的是一个稳定、有文档的配置格式或导入链接,以及它在各系统上的位置。</p>
|
||||
|
||||
<h2 id="plugins"><a class="anchor" href="#plugins">3. 插件</a></h2>
|
||||
<p>插件是 magpie 运行的 npm 包,由用户在 <span class="ui">设置</span> → <span class="ui">插件</span> 里或用 <code>magpie plugin add <package></code> 安装。<b>供应商插件</b>(OpenCode 的 <code>auth</code> 钩子或 pi 扩展包)添加一个模型来源,比如 magpie 自己不登录的订阅。<b>网关中间件</b>在请求和回复经过时改写它们。见<a href="/docs/zh/plugins">插件开发</a>。插件不能在 Agents 页面加一行,也不能写别的应用的配置,所以它不是应用注册自己的方式。</p>
|
||||
|
||||
<h2 id="not"><a class="anchor" href="#not">目前没有的</a></h2>
|
||||
<ul>
|
||||
<li>没有让别的应用不经用户就把自己注册为 Agent、或安装插件的 API 或链接:插件由用户在 magpie 里安装,Agent 行来自 magpie 源码里的适配器。</li>
|
||||
<li><code>magpie://import</code> 链接添加的是<em>供应商</em>(模型来源及其 key),不是 Agent 或应用:见 <a href="/docs/zh/import">Add to magpie</a>。</li>
|
||||
</ul>
|
||||
|
||||
<p style="margin-top:48px">有问题,或想先讨论一个适配器:提一个 <a href="https://github.com/yetone/magpie/issues">issue</a>,或来 <a href="https://discord.gg/vGSnD3ZKQF">Discord</a> 聊。</p>
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<footer><div class="wrap">
|
||||
<span>© 2026 magpie · MIT</span>
|
||||
<div class="links">
|
||||
<a href="/zh/">首页</a>
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
</div>
|
||||
</div></footer>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const next = { system: "light", light: "dark", dark: "system" };
|
||||
document.querySelectorAll("button.theme").forEach((b) => {
|
||||
b.dataset.mode = themeMode();
|
||||
b.title = "Theme: " + themeMode();
|
||||
b.addEventListener("click", () => setTheme(next[themeMode()]));
|
||||
});
|
||||
|
||||
document.querySelectorAll(".copy").forEach((b) => b.addEventListener("click", async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(document.getElementById(b.dataset.copy).textContent);
|
||||
b.textContent = "已复制"; setTimeout(() => (b.textContent = "复制"), 1400);
|
||||
} catch {}
|
||||
}));
|
||||
})();
|
||||
</script>
|
||||
<script src="/docs.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -316,6 +317,7 @@ The number of its kind (0 for none):</pre>
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -707,6 +708,7 @@ magpie plugin options model-map '{"mapping": {"fast": "deepseek/deepseek-chat"}}
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
</div>
|
||||
<div class="langs">
|
||||
@@ -271,6 +272,7 @@ magpie claude group/opus-anywhere <span class="c"># 使用它</span></pre>
|
||||
<a href="/docs/zh/intent">意图路由</a>
|
||||
<a href="/docs/zh/plugins">插件开发</a>
|
||||
<a href="/docs/zh/import">Add to magpie</a>
|
||||
<a href="/docs/zh/integrate">接入应用</a>
|
||||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||||
|
||||
Reference in new issue
Block a user