1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
|
#ifndef PANTO_H
#define PANTO_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_WIN32)
#define PANTO_EXPORT __declspec(dllexport)
#else
#define PANTO_EXPORT __attribute__((visibility("default")))
#endif
typedef struct PantoAgent PantoAgent;
typedef struct PantoStream PantoStream;
typedef struct PantoConversation PantoConversation;
typedef struct PantoSession PantoSession;
typedef struct PantoSessionStore PantoSessionStore;
typedef struct { const uint8_t *ptr; size_t len; } PantoSlice;
typedef enum { PANTO_OK = 0, PANTO_ERROR = 1 } PantoStatus;
typedef enum { PANTO_NEXT_EVENT = 0, PANTO_NEXT_DONE = 1, PANTO_NEXT_ERROR = 2 } PantoNextStatus;
typedef enum { PANTO_OPENAI_CHAT = 0, PANTO_ANTHROPIC_MESSAGES = 1 } PantoAPIStyle;
typedef enum { PANTO_REASONING_DEFAULT = 0, PANTO_REASONING_OFF, PANTO_REASONING_MINIMAL, PANTO_REASONING_LOW, PANTO_REASONING_MEDIUM, PANTO_REASONING_HIGH } PantoReasoningEffort;
typedef enum { PANTO_THINKING_DISABLED = 0, PANTO_THINKING_ENABLED, PANTO_THINKING_ADAPTIVE } PantoThinking;
typedef enum { PANTO_EFFORT_LOW = 0, PANTO_EFFORT_MEDIUM, PANTO_EFFORT_HIGH, PANTO_EFFORT_XHIGH, PANTO_EFFORT_MAX } PantoEffort;
typedef enum { PANTO_ROLE_SYSTEM = 0, PANTO_ROLE_USER, PANTO_ROLE_ASSISTANT } PantoMessageRole;
typedef enum { PANTO_BLOCK_TEXT = 0, PANTO_BLOCK_THINKING, PANTO_BLOCK_TOOL_USE, PANTO_BLOCK_TOOL_RESULT, PANTO_BLOCK_SYSTEM, PANTO_BLOCK_COMPACTION_SUMMARY } PantoContentBlockTag;
typedef enum { PANTO_SYSTEM_APPEND = 0, PANTO_SYSTEM_REPLACE } PantoSystemMode;
typedef enum { PANTO_EVENT_MESSAGE_START = 0, PANTO_EVENT_BLOCK_START, PANTO_EVENT_TOOL_DETAILS, PANTO_EVENT_CONTENT_DELTA, PANTO_EVENT_BLOCK_COMPLETE, PANTO_EVENT_MESSAGE_COMPLETE, PANTO_EVENT_PROVIDER_RETRY, PANTO_EVENT_TOOL_DISPATCH_START, PANTO_EVENT_TOOL_DISPATCH_COMPLETE, PANTO_EVENT_TURN_COMPLETE } PantoEventTag;
typedef struct { uint64_t input, output, cache_read, cache_write, reasoning; } PantoUsage;
typedef struct { PantoAPIStyle api_style; PantoSlice base_url; PantoSlice model; PantoReasoningEffort reasoning; PantoThinking thinking; PantoEffort effort; bool has_thinking_budget_tokens; uint32_t thinking_budget_tokens; bool thinking_interleaved; } PantoWireIdentity;
typedef struct { PantoSlice id, created, modified, last_user_message, base_url, model; size_t message_count; PantoAPIStyle api_style; PantoReasoningEffort reasoning; } PantoSessionInfo;
typedef struct { PantoSessionInfo *ptr; size_t len; } PantoSessionInfoList;
typedef struct { bool found; PantoSession *session; } PantoSessionResult;
typedef struct { const char *api_key, *base_url, *model; PantoReasoningEffort reasoning; uint32_t max_tokens; } PantoOpenAIChatConfig;
typedef struct { const char *api_key, *base_url, *model, *api_version; uint32_t max_tokens; PantoThinking thinking; PantoEffort effort; bool has_thinking_budget_tokens; uint32_t thinking_budget_tokens; bool thinking_interleaved; } PantoAnthropicMessagesConfig;
typedef struct { PantoAPIStyle tag; union { PantoOpenAIChatConfig openai_chat; PantoAnthropicMessagesConfig anthropic_messages; } data; } PantoProviderConfig;
typedef struct { uint32_t keep_verbatim; bool has_model; PantoProviderConfig model; bool has_compaction_prompt; const char *compaction_prompt; } PantoCompactionConfig;
typedef struct { size_t max_attempts; uint64_t initial_delay_ms, max_delay_ms; double multiplier; bool jitter; } PantoRetryConfig;
typedef struct { PantoProviderConfig provider; PantoCompactionConfig compaction; PantoRetryConfig retry; } PantoConfig;
typedef struct { PantoContentBlockTag block_type; size_t index; } PantoBlockStart;
typedef struct { size_t index; PantoSlice id, name; } PantoToolDetails;
typedef struct { size_t index; PantoSlice delta; } PantoContentDelta;
typedef struct { size_t index; PantoContentBlockTag block_type; } PantoBlockComplete;
/* One assistant message finished streaming. `message_index` indexes the
* agent's live conversation (`panto_agent_conversation`), whose tail is this
* message when the event is delivered; fetch its `block_count` blocks from
* there for full content. `usage` is the wire-reported token usage. */
typedef struct { PantoMessageRole role; size_t message_index; size_t block_count; bool has_usage; PantoUsage usage; } PantoMessageComplete;
typedef struct { bool compacted; size_t kept_turns; size_t summarized_messages; } PantoCompactionResult;
typedef struct { size_t attempt, max_attempts; uint64_t delay_ms; PantoSlice error_name; bool has_status_code; uint16_t status_code; bool has_retry_after_ms; uint64_t retry_after_ms; PantoSlice message; bool compaction; } PantoProviderRetry;
typedef struct { size_t count; } PantoToolDispatchStart;
typedef struct { size_t message_index; } PantoToolDispatchComplete;
typedef struct { PantoEventTag tag; union { PantoMessageRole message_start; PantoBlockStart block_start; PantoToolDetails tool_details; PantoContentDelta content_delta; PantoBlockComplete block_complete; PantoMessageComplete message_complete; PantoProviderRetry provider_retry; PantoToolDispatchStart tool_dispatch_start; PantoToolDispatchComplete tool_dispatch_complete; } data; } PantoEvent;
/* One content block of a message being appended. A flattened, borrow-only
* view: every slice points into the in-memory message and is valid only for
* the duration of the append_messages call. `text` carries the Text body,
* Thinking text, ToolUse input JSON, concatenated ToolResult text, or
* System/CompactionSummary text depending on `tag`. `tool_id` is the ToolUse
* id or ToolResult tool_use_id; `tool_name` is the ToolUse name; `is_error`
* is the ToolResult error flag; `system_mode` is the System block mode;
* `thinking_signature` is the Thinking signature (empty when absent), and the
* `thinking_signature_*` fields record the provider/style/model that emitted
* it. To replay a thinking block faithfully (Anthropic drops thinking blocks
* whose signature lacks a matching origin) a store must persist the signature
* AND all three origin fields, then feed them back via the message builder. */
typedef struct {
PantoContentBlockTag tag;
PantoSlice text;
PantoSlice tool_id;
PantoSlice tool_name;
bool is_error;
PantoSystemMode system_mode;
PantoSlice thinking_signature;
PantoAPIStyle thinking_signature_api_style;
PantoSlice thinking_signature_base_url;
PantoSlice thinking_signature_model;
} PantoOwnedContentBlock;
/* `api_style`/`base_url`/`model`/`reasoning` are the per-message wire identity
* (the provider that produced this message), which can drift within a single
* conversation (compaction on a different model, a mid-chat model switch). A
* store should persist these per message rather than from static config. */
typedef struct { PantoMessageRole role; bool has_usage; PantoUsage usage; bool has_metadata; PantoSlice metadata; PantoAPIStyle api_style; PantoSlice base_url; PantoSlice model; PantoReasoningEffort reasoning; const PantoOwnedContentBlock *content; size_t content_len; } PantoPersistentMessage;
typedef struct {
PantoStatus (*create)(void *ctx, PantoSession **out);
PantoStatus (*list)(void *ctx, PantoSessionInfoList *out);
void (*free_session_infos)(void *ctx, PantoSessionInfoList infos);
PantoStatus (*resolve)(void *ctx, PantoSlice id, PantoSessionResult *out);
PantoStatus (*latest)(void *ctx, PantoSessionResult *out);
PantoStatus (*load)(void *ctx, PantoSlice id, PantoConversation **out);
PantoStatus (*append_messages)(void *ctx, PantoSlice session_id, const PantoPersistentMessage *messages, size_t len);
void (*destroy)(void *ctx);
} PantoSessionStoreVTable;
PANTO_EXPORT void panto_init(void);
PANTO_EXPORT void panto_deinit(void);
PANTO_EXPORT PantoSlice panto_last_error(void);
PANTO_EXPORT void panto_error_free(PantoSlice s);
PANTO_EXPORT PantoStatus panto_conversation_create(PantoConversation **out);
PANTO_EXPORT void panto_conversation_destroy(PantoConversation *conversation);
PANTO_EXPORT size_t panto_conversation_message_count(PantoConversation *conversation);
PANTO_EXPORT PantoMessageRole panto_conversation_message_role(PantoConversation *conversation, size_t index);
PANTO_EXPORT bool panto_conversation_message_has_usage(PantoConversation *conversation, size_t index);
PANTO_EXPORT PantoUsage panto_conversation_message_usage(PantoConversation *conversation, size_t index);
PANTO_EXPORT size_t panto_conversation_message_block_count(PantoConversation *conversation, size_t message_index);
PANTO_EXPORT PantoContentBlockTag panto_conversation_block_tag(PantoConversation *conversation, size_t message_index, size_t block_index);
PANTO_EXPORT PantoSlice panto_conversation_block_text(PantoConversation *conversation, size_t message_index, size_t block_index);
PANTO_EXPORT PantoSlice panto_conversation_block_tool_id(PantoConversation *conversation, size_t message_index, size_t block_index);
PANTO_EXPORT PantoSlice panto_conversation_block_tool_name(PantoConversation *conversation, size_t message_index, size_t block_index);
PANTO_EXPORT bool panto_conversation_block_tool_result_is_error(PantoConversation *conversation, size_t message_index, size_t block_index);
PANTO_EXPORT PantoSystemMode panto_conversation_block_system_mode(PantoConversation *conversation, size_t message_index, size_t block_index);
PANTO_EXPORT PantoStatus panto_conversation_add_system_message(PantoConversation *conversation, const char *text);
PANTO_EXPORT PantoStatus panto_conversation_replace_system_message(PantoConversation *conversation, const char *text);
PANTO_EXPORT PantoStatus panto_conversation_add_user_text(PantoConversation *conversation, const char *text);
PANTO_EXPORT PantoStatus panto_conversation_add_assistant_text(PantoConversation *conversation, const char *text, bool has_usage, PantoUsage usage);
PANTO_EXPORT PantoStatus panto_conversation_add_compaction_summary(PantoConversation *conversation, const char *text);
/* Rich, block-level message builder. Accumulate content blocks, then commit
* the whole message to a conversation in one call — the path for rebuilding a
* persisted conversation that contains tool calls or thinking. The builder
* uses the same allocator as the conversation it is committed to.
*
* Usage: create -> add_* (one per block, in order) -> conversation_add_message.
* `panto_conversation_add_message` ALWAYS consumes the builder (it is freed on
* both success and failure); do not destroy it afterwards. Call
* `panto_message_builder_destroy` only to discard a builder you never commit.
* Each `text`/`signature`/etc. argument is copied. */
typedef struct PantoMessageBuilder PantoMessageBuilder;
PANTO_EXPORT PantoMessageBuilder *panto_message_builder_create(PantoMessageRole role);
PANTO_EXPORT void panto_message_builder_destroy(PantoMessageBuilder *builder);
PANTO_EXPORT PantoStatus panto_message_builder_add_text(PantoMessageBuilder *builder, const char *text);
/* `signature` may be NULL/empty when the provider emitted no signature, in
* which case the `signature_*` origin args are ignored. When a signature is
* supplied you may pass the origin (style/base_url/model) it was produced
* under so the block can be replayed; an Anthropic request drops thinking
* blocks whose signature origin does not match the request's base_url/model.
* The origin args are OPTIONAL: pass PANTO_API_STYLE 0 and NULL base_url/model
* to omit them and instead rely on the enclosing message's identity (see
* `panto_message_builder_set_identity`), which is the preferred, per-message
* source of truth — no per-block identity copy required. */
PANTO_EXPORT PantoStatus panto_message_builder_add_thinking(PantoMessageBuilder *builder, const char *text, const char *signature, PantoAPIStyle signature_api_style, const char *signature_base_url, const char *signature_model);
PANTO_EXPORT PantoStatus panto_message_builder_add_tool_use(PantoMessageBuilder *builder, const char *id, const char *name, const char *input);
PANTO_EXPORT PantoStatus panto_message_builder_add_tool_result(PantoMessageBuilder *builder, const char *tool_use_id, const char *text, bool is_error);
PANTO_EXPORT PantoStatus panto_message_builder_add_system(PantoMessageBuilder *builder, const char *text, PantoSystemMode mode);
/* Attach the wire identity (provider/style/model + reasoning) that produced
* this message. This is the per-message source of truth for replaying thinking
* signatures and is preserved through compaction, so a kept-verbatim turn is
* not re-stamped with the compaction model. Strings are copied. Optional; when
* omitted, thinking replay relies on each block's own signature origin. */
PANTO_EXPORT PantoStatus panto_message_builder_set_identity(PantoMessageBuilder *builder, PantoAPIStyle api_style, const char *base_url, const char *model, PantoReasoningEffort reasoning);
/* Commit the builder's blocks as one message of the builder's role. Consumes
* (frees) the builder. `usage` applies to assistant turns; pass has_usage=false
* otherwise. */
PANTO_EXPORT PantoStatus panto_conversation_add_message(PantoConversation *conversation, PantoMessageBuilder *builder, bool has_usage, PantoUsage usage);
PANTO_EXPORT PantoStatus panto_session_create_null(PantoSession **out);
PANTO_EXPORT PantoStatus panto_session_create(PantoSessionStore *store, PantoSessionInfo info, PantoSession **out);
PANTO_EXPORT void panto_session_destroy(PantoSession *session);
PANTO_EXPORT PantoStatus panto_session_store_create(void *ctx, const PantoSessionStoreVTable *vtable, PantoSessionStore **out);
PANTO_EXPORT void panto_session_store_destroy(PantoSessionStore *store);
PANTO_EXPORT PantoStatus panto_null_store_create(PantoSessionStore **out);
PANTO_EXPORT PantoStatus panto_file_system_jsonl_store_create(const char *dir, const char *metadata, PantoSessionStore **out);
PANTO_EXPORT PantoStatus panto_session_store_create_session(PantoSessionStore *store, PantoSession **out);
PANTO_EXPORT PantoStatus panto_session_store_list(PantoSessionStore *store, PantoSessionInfoList *out);
PANTO_EXPORT void panto_session_info_list_destroy(PantoSessionInfoList infos);
PANTO_EXPORT PantoStatus panto_session_store_resolve(PantoSessionStore *store, const char *id, PantoSessionResult *out);
PANTO_EXPORT PantoStatus panto_session_store_latest(PantoSessionStore *store, PantoSessionResult *out);
PANTO_EXPORT PantoStatus panto_session_load(PantoSession *session, PantoConversation **out);
PANTO_EXPORT PantoStatus panto_agent_create(const PantoConfig *config, PantoSession *session, PantoConversation *conversation, PantoAgent **out);
PANTO_EXPORT PantoConversation *panto_agent_conversation(PantoAgent *agent);
PANTO_EXPORT void panto_agent_destroy(PantoAgent *agent);
PANTO_EXPORT PantoSlice panto_agent_session_id(PantoAgent *agent);
PANTO_EXPORT PantoStatus panto_agent_set_config(PantoAgent *agent, const PantoConfig *config);
PANTO_EXPORT PantoStatus panto_agent_add_system_message(PantoAgent *agent, const char *text);
PANTO_EXPORT PantoStatus panto_agent_set_system_prompt(PantoAgent *agent, const char *text);
PANTO_EXPORT PantoStatus panto_agent_compact(PantoAgent *agent, const char *override_system_prompt, const char *extra_instructions, PantoCompactionResult *out);
/* Open a turn from a user-role message built with the PantoMessageBuilder API
* (the same builder used to rebuild persisted messages): one `add_text` block
* for a plain chat turn, or `add_tool_result` blocks to resume after the
* embedder handled tool calls itself. Consumes `user_message` (the builder is
* destroyed); do not reuse or destroy it afterwards. */
PANTO_EXPORT PantoStatus panto_agent_run(PantoAgent *agent, PantoMessageBuilder *user_message, PantoStream **out);
/* Reset a failed stream back to its turn-open boundary so the caller can
* resume panto_stream_next() after changing the agent config (e.g. catch a
* terminal bad-request, swap the provider config, and retry the SAME turn
* without re-appending the user message). Errors if the stream has not
* failed. */
PANTO_EXPORT PantoStatus panto_stream_reopen(PantoStream *stream);
PANTO_EXPORT void panto_stream_destroy(PantoStream *stream);
PANTO_EXPORT PantoNextStatus panto_stream_next(PantoStream *stream, PantoEvent *out);
PANTO_EXPORT void panto_event_free(PantoEvent *event);
/* Go/callback-oriented tool ABI. */
typedef struct { PantoSlice name, description, schema_json; } PantoToolDecl;
typedef struct { PantoSlice media_type; bool has_media_type; PantoSlice data; } PantoMediaPart;
typedef enum { PANTO_RESULT_TEXT = 0, PANTO_RESULT_MEDIA = 1 } PantoResultPartTag;
typedef struct { PantoResultPartTag tag; union { PantoSlice text; PantoMediaPart media; } data; } PantoResultPart;
typedef struct { PantoResultPart *ptr; size_t len; } PantoResultParts;
typedef struct { PantoSlice tool_name, input; } PantoToolCall;
typedef struct { bool ok; PantoResultParts parts; PantoSlice error; } PantoToolCallResult;
typedef PantoStatus (*PantoToolInvokeFn)(void *ctx, PantoSlice input, PantoResultParts *out);
typedef PantoStatus (*PantoToolSourceInvokeBatchFn)(void *ctx, const PantoToolCall *calls, PantoToolCallResult *results, size_t len);
typedef void (*PantoToolDestroyFn)(void *ctx);
PANTO_EXPORT PantoStatus panto_agent_register_tool(PantoAgent *agent, PantoToolDecl decl, void *ctx, PantoToolInvokeFn invoke, PantoToolDestroyFn destroy);
PANTO_EXPORT PantoStatus panto_agent_register_tool_source(PantoAgent *agent, PantoSlice name, const PantoToolDecl *decls, size_t len, void *ctx, PantoToolSourceInvokeBatchFn invoke_batch, PantoToolDestroyFn destroy);
PANTO_EXPORT PantoResultParts panto_result_parts_text(const char *text);
PANTO_EXPORT void panto_result_parts_destroy(PantoResultParts parts);
#ifdef __cplusplus
}
#endif
#endif
|