Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions internal/x402/paymentrequired.go
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,11 @@ func inferenceCopy(url string, d PaymentDisplay) typeCopy {
other := fmt.Sprintf(
"Read https://obol.org/llms.txt to learn how Obol's x402 micropayments work. "+
"I want to use the remote LLM at %s (model %s) as a paid OpenAI-compatible "+
"chat-completions endpoint. Pre-sign a budget of EIP-3009/Permit2 authorizations "+
"and POST chat-completions bodies with the X-PAYMENT header attached.",
url, model,
"chat-completions endpoint. Pre-sign a budget of EIP-3009/Permit2 authorizations, "+
"then POST to `/v1/chat/completions` with a body like "+
`{"model":"%s","messages":[{"role":"user","content":"<your prompt>"}]} `+
"and the X-PAYMENT header attached.",
url, model, model,
)

return typeCopy{
Expand All @@ -403,6 +405,17 @@ func inferenceCopy(url string, d PaymentDisplay) typeCopy {
PrimaryPayload: cmd,
PromptObol: prompt,
PromptOther: other,
ChatCompletionsNote: "Direct HTTP buyers use OpenAI-style chat-completions. A minimal paid request looks like:",
ChatCompletionsBody: fmt.Sprintf(`POST %s/v1/chat/completions
Content-Type: application/json
X-PAYMENT: <pre-signed-EIP-3009-or-Permit2-voucher>

{
"model": "%s",
"messages": [
{"role": "user", "content": "<your prompt>"}
]
}`, strings.TrimSuffix(url, "/"), model),
}
}

Expand Down Expand Up @@ -435,15 +448,17 @@ X-PAYMENT: <pre-signed-EIP-3009-or-Permit2-voucher>
prompt := fmt.Sprintf(
"Use the buy-x402 skill's `pay` command to call the Obol Agent at %s%s. "+
"This is an *agent*, not a raw model — it has its own skills, tools, and memory. "+
"Include a clear instruction in the chat-completions body so the agent knows what to do.",
"Send an OpenAI-style chat-completions body with a clear user message in `messages` "+
"(see the example under Pay manually below). Do not pay without a real instruction.",
url, modelLine,
)

other := fmt.Sprintf(
"Read https://obol.org/llms.txt to learn how Obol's x402 micropayments work. "+
"Help me call the Obol Agent at %s%s — it's an autonomous agent (tools + skills + memory), "+
"not a raw LLM. POST OpenAI-style chat-completions JSON with a real prompt in `messages`, "+
"attach a signed EIP-3009/Permit2 authorization as `X-PAYMENT`, and report what the agent does.",
"not a raw LLM. POST OpenAI-style chat-completions JSON: `messages` must include a user turn "+
"with your task (e.g. {\"role\":\"user\",\"content\":\"Summarise …\"}), plus a signed "+
"EIP-3009/Permit2 voucher as `X-PAYMENT`. Report what the agent returns.",
url, modelLine,
)

Expand Down Expand Up @@ -477,7 +492,10 @@ func httpCopy(url string, d PaymentDisplay) typeCopy {
if d.NetworkLabel != "" {
netClause = " Network: " + d.NetworkLabel + "."
}
prompt := fmt.Sprintf("Use the buy-x402 skill's `pay` command to call %s once.%s%s", url, priceClause, netClause)
prompt := fmt.Sprintf(
"Use the buy-x402 skill's `pay` command to call %s once.%s%s "+
"Use the method and payload the seller documents.",
url, priceClause, netClause)

priceWord := "the listed price"
if d.PriceDisplay != "" {
Expand Down
3 changes: 3 additions & 0 deletions internal/x402/paymentrequired_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ func TestHTMLAware_InferenceShowsCLIPrimaryAndDescription(t *testing.T) {
// in "operator's hardware" appears raw, not entity-encoded.
mustContain(t, body, "agent runs locally")
mustContain(t, body, "remote operator's hardware")
mustContain(t, body, "OpenAI-style chat-completions")
mustContain(t, body, `/v1/chat/completions`)
}

// Agent offers should explain that the buyer is paying an autonomous
Expand Down Expand Up @@ -282,6 +284,7 @@ func TestHTMLAware_HTTPKeepsLegacyCopy(t *testing.T) {
body := w.Body.String()
mustContain(t, body, "Pay with your Obol Agent")
mustContain(t, body, "buy-x402 skill")
mustContain(t, body, "Use the method and payload the seller documents")
if strings.Contains(body, "obol buy inference") {
t.Errorf("http-type 402 page should NOT show the inference CLI primary card")
}
Expand Down
3 changes: 3 additions & 0 deletions web/public-storefront/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion web/public-storefront/src/components/ServiceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ function BuyViaObolAgent({ service }: { service: Service }) {
}

// http (default): legacy single-shot pay.
const prompt = `Use the buy-x402 skill's \`pay\` command to call ${service.endpoint} once. Pay ${service.price} on ${service.network}. Report what it returns.`;
const prompt = `Use the buy-x402 skill's \`pay\` command to call ${service.endpoint} once. Pay ${service.price} on ${service.network}. Use the method and payload the seller documents. Report what it returns.`;
return (
<div className="space-y-2">
<p className="text-xs text-text-muted">
Expand Down
Loading