Merge remote app attribution branch

Resolve the RFC and implementation to defer OpenRouter-specific attribution headers and keep mandatory attribution to User-Agent only.
This commit is contained in:
Tianyi Cui
2026-07-04 22:47:53 +08:00
692 changed files with 49021 additions and 4970 deletions

View File

@@ -13,7 +13,8 @@
import { stream as piStream } from '@earendil-works/pi-ai'
import type { Model } from '@earendil-works/pi-ai'
import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm'
import { attributionHeaders, LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm'
import { CallId } from '@deepseek-ai/dsh-llm'
import type { GenerateOptions, StreamChunk, ToolSchema } from '@deepseek-ai/dsh-llm'
import { toPiContext, toStreamChunks } from './convert.ts'
@@ -69,8 +70,8 @@ type Payload = {
stop?: unknown
}
function rawToolArguments(options: GenerateOptions): Map<string, string> {
const raw = new Map<string, string>()
function rawToolArguments(options: GenerateOptions): Map<CallId, string> {
const raw = new Map<CallId, string>()
for (const message of options.messages) {
if (message.role !== 'assistant') continue
for (const block of message.content) {
@@ -116,7 +117,7 @@ function patchPayload(payload: unknown, options: GenerateOptions, reasoning: PiA
for (const call of message.tool_calls ?? []) {
/* v8 ignore next -- malformed pi-ai payload guard: real tool calls always carry a string id */
if (typeof call.id !== 'string') continue
const raw = rawById.get(call.id)
const raw = rawById.get(CallId(call.id))
/* v8 ignore next -- pi-ai always emits a function object for assistant tool_calls; guard malformed payloads defensively */
if (raw !== undefined && call.function !== undefined) call.function.arguments = raw
}
@@ -170,6 +171,9 @@ export class PiAiAdapter extends LlmAdapter {
try {
const events = piStream(model, toPiContext(options), {
apiKey: this.options.apiKey,
// pi-ai merges caller headers last over its provider defaults, so the
// harness attribution always reaches the wire.
headers: attributionHeaders(),
...options.temperature !== undefined ? { temperature: options.temperature } : {},
...options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {},
signal: controller.signal,

View File

@@ -57,7 +57,7 @@ function parseArguments(raw: string): Record<string, unknown> {
* same id.
*/
export function toPiContext(options: GenerateOptions): PiContext {
const toolNames = new Map<string, string>()
const toolNames = new Map<CallId, string>()
const messages: PiMessage[] = []
for (const message of options.messages) {