@anna-ai/app-runtime (英语)
Anna Apps 的浏览器运行时间 SDK. 每个Anna导入的本地ES模块
应用程序 iframe; 导出 AnnaAppRuntime,代表RPC调用
postMessage 到主机页面(制作中的Anna仪表板
anna-app dev (b) 利用当地发展手段.
0.3.1+ (单位:千美元): SDK是一个纯正的ES模块. 装入
<script type="module">+ 键import { AnnaAppRuntime } from "…"旧的 经典<script src>+ 键window.AnnaAppRuntime将全局删除.
在 Anna App 包内使用
使您的条目脚本成为模块并直接导入 SDK :
<!-- index.html -->
<script src="./app.js" type="module"></script>
// app.js
import { AnnaAppRuntime } from "/static/anna-apps/_sdk/latest/index.js";
const anna = await AnnaAppRuntime.connect();
await anna.window.set_title({ title: "My App" });
const out = await anna.tools.invoke({
tool_id: "tool-twitter-handle-twitter-tool-abcd1234",
method: "post_tweet",
args: { text: "hi" },
});
anna.on("entry_payload", (p) => console.log(p));
不要用内线
<script>。 。 。 . 捆绑的CSP是script-src 'self' <sdk-origin>没有unsafe-inline/nonce,所以任何一个 内置脚本(包括将 SDK 连接到 )被屏蔽. 总是让你的条目 外部 模块 (单位:千美元)<script src="app.js" type="module">和import里面是SDK 里面是那个.
电线信封
{ kind: "req"|"res"|"event", id, ns, method, args | result | error }
主机验证每一次通话 与应用程序显示的ACL之前 转发到后端.
RPC 超时
每个呼叫都有每个名称空间的默认;应用程序可以覆盖每个呼叫:
// Use the namespace default (image: 180s)
const out = await anna.image.generate({ prompt: "..." });
// Override for one call (e.g. high-res / many images)
const out = await anna.image.generate(
{ prompt: "...", n: 4, size: "2048x2048" },
{ timeoutMs: 240000 }
);
| 命名空间 | 默认 | 理由 |
|---|---|---|
image.* | 180 s (简体中文) | 匹配主机的上游提供者超时( 种子 / Quen / 原生) |
upload.* | 120 s (简体中文) | 大型多部分/谈判+确认 |
agent.* | 300 s (简体中文) | session.run 循环模式 + 工具 多个回合 |
llm.* | 180 s (简体中文) | 长期完成/大背景 |
| 其他的都一样 | 30s (中文(简体) ) | 存储 / 聊天 / 窗口 / prefs / fs / 工具 / 文物 |
超时是 客户端 截止日期。 主机桥接没有
中止信号——后端总是运行到完成,但超时
承诺拒绝 Error("RPC <ns>.<method> timed out after Nms")
并放弃最终反应.
剧本来自何处
| 环境 | 来源 |
|---|---|
| 生产(安娜仪表板) | matrix-nexus 静态服务器, 路径 /static/anna-apps/_sdk/<version>/index.js |
本地 dev (anna-app dev(中文(简体) ) | @anna-ai/cli 调用服务器从它的 node_modules 并服务同一路径 |
捆绑的作者也是这样写的
import { AnnaAppRuntime } from "/static/anna-apps/_sdk/latest/index.js"
; URL是主机合同的一部分.
版本
此软件包的版本音轨 dispatcher_version (线条协议). 见
packages/VERSIONS.md 完全针状和起伏
程序.
目录
单个文件 dist/index.js原生ES模块(终到
export { AnnaAppRuntime, ... }),没有建设步骤,直接运行于任何现代
浏览器通过 <script type="module"> 编号 : import。 。 。 .
代理运行输出封顶( V0. 26. 0)
主机为: 1.1.0-β.200 / 调度器 0.30 (论坛#403). 按呼输出
上限 agent.session.run 实值 min(model output cap, 65 536) 被夹住
模型的上下文预算储备金(占上下文的10%,——65 536)——例如:65 536
1M-context模型,20000在200K-context模型上. 逐项披露
现在,最后信息被缩短:
const sess = await anna.agent.session({ submode: "auto" });
for await (const ev of sess.run({ content: prompt, maxTokens: 16384 })) {
if (ev.event === "run_meta") console.log("cap:", ev.max_output_tokens);
const tc = ev.choices?.[0]?.delta?.task_complete;
if (tc?.truncated) {
// status is still "completed": the graph ended normally, but the final
// assistant message hit the per-call cap. Continue in the same session:
// sess.run({ content: "continue exactly from: " + tail })
}
}
maxTokens?(1.65536) 仅能 下调 封顶——永远不要在站台上方 型号。 关闭使用该模型自己的输出盖.task_complete.stop_reason∈endTurn | maxTokens | contentFilter | toolUse(设置为llm.complete是因为stopReason(三);truncated === (stop_reason === "maxTokens")。 。 。 .- 旧主机省略
max_output_tokens编号 :stop_reason编号 :truncated。 。 。 .
主机上下文:主题和语言(v0.24.0)
仪表盘告诉每个应用程序 主题 财务报告和审定财务报表 UI 语言 用户
选中, 因此 Apps 不再猜测 prefers-color-scheme 编号 : navigator.language。 。 。 .
不提供明确的赠款——这些是单向主播——应用广播(如:
auth.refresh),从不调用RPC方法.
const anna = await AnnaAppRuntime.connect();
anna.theme; // 'light' | 'dark' — already correct before the first paint
anna.themeMode; // 'light' | 'dark' | 'system' — what the user picked
anna.locale; // 'en' | 'zh-CN' | … — dashboard UI language
// Subscribe; both replay the current value once, then fire on every change.
anna.onThemeChange(({ theme }) => { document.documentElement.dataset.theme = theme; });
anna.onLocaleChange(({ lang }) => i18n.setLocale(lang));
线条细节: Iframe URL 上的初始值
(单位:千美元)…?wid=&t=&theme=light&themeMode=system&lang=zh-CN并同步读取
内 connect(); 现场更改以 {kind:"event", event:"theme.changed", payload:{theme, mode}} 财务报告和审定财务报表 {event:"locale.changed", payload:{lang}}。旧宿主
- SDK回落到 prefers-color-scheme 财务报告和审定财务报表
navigator.language,因此保留 CSS 的倒置以备独立预览 :
:root[data-theme="light"] { /* light tokens */ }
:root[data-theme="dark"] { /* dark tokens */ }
@media (prefers-color-scheme: dark) { :root:not([data-theme]) { /* dark tokens */ } }
媒体生成(v0.20
Async 视频任务 + 同步音频, 由每个应用程序打开 media_grant
(显然必须宣布) ui.host_api.video 编号 : .audio:
// One-shot sugar: submit + media_job events + getJob polling fallback
const job = await anna.video.generateAwait(
{ prompt: "Aerial drone shot over a misty pine forest", durationSec: 5,
resolution: "768p", clientTag: "scene-3-broll" },
{ onProgress: (p) => bar.update(p), signal: abortController.signal }
);
video.src = job.result.url; // presigned GET, ~30 min TTL
// URL expired (403)? Re-sign via getJob:
const fresh = await anna.video.getJob({ jobId: job.jobId });
// Raw job API: generate / getJob / cancelJob / listJobs
const { jobId, estimatedCostCU } = await anna.video.generate({ prompt: "..." });
// Sync TTS / STT
const speech = await anna.audio.speak({ text: "Welcome!", voice: "Cherry" });
const { text } = await anna.audio.transcribe({ audioUrl: "https://..." });
// Capability discovery: models, parameter space, CU pricing, remaining quota
const catalog = await anna.llm.catalog({ serviceType: "video_gen" });
音乐创作 (v0.23
Async 音乐工作( Y)mjob_…完全一样的工作API, media_job 活动及
以视频形式支付退款。 要求数 ui.host_api.music + 键
每个应用程序 media_grant.music。 。 。 .
// Capability discovery first — models differ (lyrics vs prompt-embedded lyrics,
// fixed vs range length, mp3 vs wav, reference image …). Nothing is silently
// downgraded: an unsupported field fails with MUSIC_FEATURE_UNSUPPORTED (-32165)
// and error.data.alternatives lists models that do support it.
const { models } = await anna.llm.catalog({ serviceType: "music_gen" });
const m = models.find(x => x.isPlatformDefault); // fal-ai/minimax-music/v2.6
const song = await anna.music.generateAwait(
{ prompt: "melancholic cinematic piano, slow ~70 BPM, rainy night",
lyrics: "[Verse 1]\n…\n[Chorus]\n…", // only when m.capabilities.lyrics
model: m.modelName,
clientTag: "scene-3-score",
idempotencyKey: "canvas-node-8f3a-v2" }, // 24h replay → same job, no re-charge
{ onProgress: (p) => bar.update(p), signal: abortController.signal }
);
audio.src = song.result.url; // presigned GET, ~30 min TTL; song.result.lyrics when returned
song.idempotentReplay; // true when the key hit an existing job
// Range-length models (ElevenLabs / Stable Audio / ACE-Step) take durationSec;
// fixed-length models (MiniMax / Lyria) reject it. Instrumental-only models
// (Stable Audio) reject lyrics and instrumental:false.
await anna.music.generate({ prompt: "ambient pad", model: "fal-ai/ace-step", durationSec: 30, instrumental: true });
图像 async 任务和重新签名( v0. 25. 0)
anna.image.generate 编号 : anna.image.edit 保持同步和不变。 对于
2K/4K或 quality: "high" 使(60-120 s)选择相同的合成工作形状
作为视频/音乐—— ijob_… 编号, media_job 事件、预先持有 —— 结算
实际图像数量) ——退款——并重新签署任何已付文物
7天而不是在30min URL TTL之后丢失. 要求数
ui.host_api.image 列出新的方法名称+现有的 image_grant
(单位:千美元)allowGenerate 编号 : allowEdit;没有新的赠款).
// Same request body as image.generate, plus clientTag / idempotencyKey.
const job = await anna.image.generateAwait(
{ prompt: "A bold art-deco poster of the planet Mars", n: 2,
model: "fal-ai/nano-banana-2", aspect_ratio: "9:16", params: { resolution: "4K" },
clientTag: "poster-v3", idempotencyKey: `poster-v3:${attempt}` }, // 24h replay → same job, no re-charge
{ onProgress: (p) => status.textContent = `${p.state} · queue ${p.queuePosition ?? "-"}`,
signal: abortController.signal } // → image.cancelJob
);
job.result.images; // [{url, r2Key, mimeType, width, height, expiresIn}] — url re-signed by getJob
job.billedCostCU; // settled on actual output (≤ estimatedCostCU)
// Edit (optionally mask-based) as a job:
await anna.image.editAwait({ image_url, prompt: "replace the sky with aurora", mask_url, model: "openai/gpt-image-2" });
// Reload recovery / correlation — same four-pack as video / music:
const { jobs } = await anna.image.listJobs({ state: "running", clientTag: "poster-v3" });
const fresh = await anna.image.getJob({ jobId }); // every result.images[].url re-signed (~30 min)
// Re-sign already-paid artifacts (sync `images[].r2_key` or job `r2Key`), up to 7 days:
const { urls } = await anna.image.resign({ r2Keys: [img.images[0].r2_key, job.result.images[0].r2Key] });
// One key that is not yours → whole call fails with IMAGE_ASSET_NOT_FOUND (details.data.index).
表面错误为 HostRpcError 名称 : IMAGE_JOB_NOT_FOUND, (中文(简体) ),
IMAGE_JOB_NOT_CANCELLABLE, (中文(简体) ), IMAGE_CONTENT_REJECTED (同时替换同步)
路径的前 APP_PROVIDER_ERROR 供养者安全拒绝者,
IMAGE_JOB_EXPIRED, (中文(简体) ), IMAGE_ASSET_NOT_FOUND· ; error.details.data 现在携带
allowed 编号 : alternatives 编号 : index。 。 。 . anna.llm.catalog 图像条目报告
capabilities.jobs 还有, 对于 image_edit真实的 options.n.max。 。 。 .
模型认知选项(v0.220
每个 TTS / 图像模型描述它的取键在 capabilities.options
(schema v2:) (中文(简体) ) {type, min, max, step, values, default, scope, …}) (中文(简体) ). 传出任何
他们进入 params; 一个模型不声明的密钥会被丢弃并重现到
ignoredParams从不收取附加费.
const { models } = await anna.llm.catalog({ serviceType: "tts", voiceLanguage: "zh" });
const tts = models.find(m => m.isUserPreferred) ?? models.find(m => m.isPlatformDefault);
const out = await anna.audio.speak({
text: "Welcome!",
model: tts.modelName,
voice: tts.capabilities.defaultVoice,
params: { stability: 0.3, similarity_boost: 0.8 }, // keys from capabilities.options
});
out.params; // what reached the provider
out.ignoredParams; // e.g. ["similarity_boost"] on a model that only exposes stability
// Image: explicit model + size policy. `fit: "strict"` fails with
// IMAGE_SIZE_UNSUPPORTED (+ data.allowed) instead of snapping to the nearest size.
const img = await anna.image.generate({
prompt: "A bold art-deco poster of the planet Mars",
model: "fal-ai/nano-banana-2",
aspect_ratio: "9:16",
params: { resolution: "2K" },
fit: "strict",
});
img.size; img.sizeAdjusted; img.images[0].width; // real decoded dimensions
渲染器 capabilities.options 作为形式与共享
ModelOptionsForm
组件 — 平台使用的相同文件 /llm 在开发者中
指挥所游乐场.
费用为 预收 估计数中 estimatedCostCU 财务报告和审定财务报表
失败/离职/期满时全额退还。 中止 generateAwait 取消数
任务(队列=全额退款;运行取消是最佳效果).