# Use Gyblos from your code

Run the local buyer and point OpenAI or Anthropic clients at it to chat with a Gyblos seller.

Source: https://docs.gyblos.com/en/buyers/use-from-code

The buyer is a small server on your own computer, at `http://127.0.0.1:4031`. It speaks the OpenAI and
Anthropic APIs, so programs written for those services can use a Gyblos seller by changing their base URL.
For each request it gets a signed free quote from the seller and streams the answer back over a direct
peer-to-peer connection.

## What works today

| Feature                                                              | Beta status                             |
| -------------------------------------------------------------------- | --------------------------------------- |
| Whole conversations: system message, history, new message            | Yes, up to 1024 messages                |
| OpenAI `POST /v1/chat/completions` and Anthropic `POST /v1/messages` | Yes, whole answers and streaming        |
| `GET /v1/models`                                                     | Yes, the model your buyer asks for      |
| Free offers                                                          | Yes                                     |
| Tools (function calling), both formats, whole answers and streaming  | Yes, see [Tools](#tools)                |
| Claude Code                                                          | Only with a seller on a larger model    |
| Cursor agent mode                                                    | No: it calls from Cursor's cloud        |
| Images, structured output, several answers                           | Not yet: refused with a clear 400 error |
| Paid offers                                                          | Outside this guide                      |

A seller serves each buyer up to **30 answers per minute** by default. An answer is at most **4096
tokens** unless you raise the buyer's limit (up to 32768); a longer request is served up to that limit and
ends with finish reason `length`.

Your prompt reaches the seller you configured, whose machine runs the model and can read it. Do not send
passwords, keys or other secrets.

## Before you start

- An invitation with a private **relay token**.
- The **NodeId** and **owner wallet** of the seller you will use, from a source you trust (the seller
  prints both with `gyblos doctor`). The seller must be registered; the buyer checks its identity on
  Solana devnet.
- Git, [Node.js](https://nodejs.org) 24.22.1 or later, and pnpm 10 (`corepack enable` provides it).

In the beta the buyer runs from the source repository; the installer and the npm package contain the
seller only.

## Install and configure

```sh
git clone https://github.com/Gyblos/gyblos-node.git
cd gyblos-node
pnpm install --frozen-lockfile
pnpm buyer:init
```

`buyer:init` asks for the seller's NodeId and owner wallet, shows the settings (model `qwen3-0.6b`,
answers up to 4096 tokens, free quotes only) and saves them after you confirm. It sends nothing to the
seller or the chain. `pnpm buyer:init --advanced` also sets the model, the answer limit and the endpoints.

## Start the buyer

Type the relay token without showing it, then start the buyer in the same terminal:

```sh
read -rs GYBLOS_RELAY_TOKEN && export GYBLOS_RELAY_TOKEN
pnpm buyer:free
```

Leave it running. In another terminal, check that it answers:

```sh
curl http://127.0.0.1:4031/v1/models
```

The list names the one model your buyer asks for; use that name as `model` in your requests. For a quick
conversation in the terminal instead, run `pnpm buyer:chat`, which starts its own buyer.

## Send a conversation

```sh
curl http://127.0.0.1:4031/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{
    "model": "qwen3-0.6b",
    "messages": [
      {"role": "system", "content": "Answer in one short sentence."},
      {"role": "user", "content": "My name is Ana."},
      {"role": "assistant", "content": "Nice to meet you, Ana!"},
      {"role": "user", "content": "What is my name?"}
    ]
  }'
```

### OpenAI SDK

Use base URL `http://127.0.0.1:4031/v1`. The free buyer does not check the API key, but the SDK needs a
non-empty one.

```js
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'http://127.0.0.1:4031/v1',
  apiKey: 'local',
});
const stream = await client.chat.completions.create({
  model: 'qwen3-0.6b',
  stream: true,
  messages: [
    { role: 'system', content: 'Be brief.' },
    { role: 'user', content: 'My dog is called Rocky.' },
    { role: 'assistant', content: 'Got it: your dog is Rocky.' },
    { role: 'user', content: "What is my dog's name?" },
  ],
});
for await (const chunk of stream)
  process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
```

### Anthropic SDK

Use base URL `http://127.0.0.1:4031` (without `/v1`). `max_tokens` is required, as with Anthropic.

```js
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({
  baseURL: 'http://127.0.0.1:4031',
  apiKey: 'local',
});
const message = await client.messages.create({
  model: 'qwen3-0.6b',
  max_tokens: 200,
  system: 'Be brief.',
  messages: [
    { role: 'user', content: 'My dog is called Rocky.' },
    { role: 'assistant', content: 'Got it: your dog is Rocky.' },
    { role: 'user', content: "What is my dog's name?" },
  ],
});
console.log(message.content[0].text);
```

## Tools

Send `tools` as each API defines them. When the model wants a tool, the answer ends with
`finish_reason: "tool_calls"` (Anthropic: `stop_reason: "tool_use"`) and carries the call. Your program
runs the tool and sends the result back in the next request, with the whole conversation:

```js
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'http://127.0.0.1:4031/v1',
  apiKey: 'local',
});
const tools = [
  {
    type: 'function',
    function: {
      name: 'get_time',
      description: 'Current local time in a city',
      parameters: {
        type: 'object',
        properties: { city: { type: 'string' } },
        required: ['city'],
      },
    },
  },
];
const messages = [
  { role: 'user', content: 'What time is it in Santiago? Use the tool.' },
];
const first = await client.chat.completions.create({
  model: 'qwen3-0.6b',
  messages,
  tools,
});
const call = first.choices[0].message.tool_calls[0];
// Run the tool yourself, then send its result back.
messages.push(first.choices[0].message, {
  role: 'tool',
  tool_call_id: call.id,
  content: '15:42',
});
const second = await client.chat.completions.create({
  model: 'qwen3-0.6b',
  messages,
  tools,
});
console.log(second.choices[0].message.content);
```

With the Anthropic SDK, send `tools` with `input_schema`, read the `tool_use` blocks and answer with
`tool_result` blocks, as with Anthropic. The buyer asks only sellers that serve tools; if none does, it
answers 503 and says so.

The default model, qwen3:0.6b, handles simple calls like this one. Agents that send many tools and long
instructions, such as Claude Code, need a seller on a larger model through the OpenAI or Anthropic engine.

## Options and errors

Options that clients send by default and that do not change the answer are accepted and ignored, such as
`temperature`, `top_p`, `user`, `metadata`, `stream_options` and, in the Anthropic format, `thinking`
(thinking is always off). Options that would change it are refused unless they hold their neutral value:
`response_format` (`text`), `n` (1), `stop` and `stop_sequences`, and `logprobs`. Images are always
refused.

| Answer | Meaning                                                                                                                                                                  |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 400    | The request uses something not supported yet; the message and `param` name it.                                                                                           |
| 503    | No seller is available right now. With `Retry-After`, the seller failed recently or hit its per-minute limit: the OpenAI and Anthropic SDKs wait and retry on their own. |
| 504    | The seller did not answer in time.                                                                                                                                       |

Errors use each API's own shape, so SDKs raise their usual exceptions.
