Repository navigation
Expand file tree
/
Copy pathclient.h
More file actions
224 lines (193 loc) · 6.62 KB
/
Copy pathclient.h
File metadata and controls
224 lines (193 loc) · 6.62 KB
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
223
224
// Copyright 2026 Bontal LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#pragma once
#include <future>
#include <memory>
#include <optional>
#include <string>
#include "typesafe/transport.h"
#include "typesafe/types.h"
#include "typesafe/typesafe_error.h"
namespace typesafe
{
struct TypeSafeClientConfig
{
std::optional<std::string> api_key;
std::string base_url = "https://api.typesafe.ai";
std::string default_model = "jev-latest";
int timeout_ms = 10000;
int max_retries = 2;
int retry_timeout_ms = 30000;
bool openrouter = false;
};
class TypeSafeClient;
/**
* @brief Instantiates a TypeSafeClient.
*
* Automatically falls back to environment variables (e.g. TYPESAFE_API_KEY)
* if values are omitted.
*/
class TypeSafeClientBuilder
{
public:
TypeSafeClientBuilder();
/**
* @brief Sets the API key.
*/
TypeSafeClientBuilder &api_key(const std::string &key);
/**
* @brief Overrides the default base URL (https://api.typesafe.ai).
*/
TypeSafeClientBuilder &base_url(const std::string &url);
TypeSafeClientBuilder &model(const std::string &model);
/**
* @brief Sets the timeout for each attempt in milliseconds (default:
* 10000); SystemOneRequest::timeout_ms overrides it per request.
*/
TypeSafeClientBuilder &timeout(int ms);
/**
* @brief Sets the maximum number of automatic retries (default: 2).
*/
TypeSafeClientBuilder &max_retries(int retries);
/**
* @brief Sets the total time budget for one call in milliseconds,
* counting every attempt and every wait between them (default: 30000;
* 0 disables the limit).
*
* No retry starts when the time spent plus its wait would reach the
* budget; the call then throws the last attempt's error. A running
* attempt keeps its full timeout. With the limit disabled, a wait
* longer than 60 s also ends the call.
*/
TypeSafeClientBuilder &retry_timeout(int ms);
/**
* @brief Experimental: sends System One requests through OpenRouter.
*
* Sets the API key, the base URL https://openrouter.ai/api (so calls
* go to https://openrouter.ai/api/v1/systemone) and the model
* "typesafe/jev-1.13". Call model() afterwards to pick another model;
* calling base_url() afterwards leaves OpenRouter mode.
* listModels() throws ValidationError in this mode, because
* OpenRouter's /v1/models is a different endpoint.
*/
TypeSafeClientBuilder &openrouter(const std::string &key);
/**
* @brief Injects a custom HTTP transport engine.
*
* Replaces the default libcurl implementation.
*/
TypeSafeClientBuilder &transport(std::unique_ptr<Transport> transport);
/**
* @brief Finalizes configuration and instantiates the client.
*
* @throws AuthenticationError if the API key is missing, or contains
* whitespace, control, or non-ASCII characters after trimming.
* @throws ValidationError if the base URL is not absolute http(s), the
* model is empty, the timeout is not positive, or max_retries or
* retry_timeout is negative.
* @throws TypeSafeError if no transport is injected and libcurl is
* disabled.
*/
TypeSafeClient build();
private:
TypeSafeClientConfig _config;
std::unique_ptr<Transport> _transport;
};
/**
* @brief The main SDK client for TypeSafe.
*/
class TypeSafeClient
{
public:
static TypeSafeClientBuilder builder();
/**
* @brief Internal constructor used by the builder.
*/
TypeSafeClient(TypeSafeClientConfig config,
std::unique_ptr<Transport> transport);
~TypeSafeClient();
TypeSafeClient(const TypeSafeClient &) = delete;
TypeSafeClient &operator=(const TypeSafeClient &) = delete;
TypeSafeClient(TypeSafeClient &&) = default;
TypeSafeClient &operator=(TypeSafeClient &&) = default;
/**
* @brief Evaluates a SystemOne Request.
* @throws TypeSafeError on network, authentication, or API failure.
*/
SystemOneResponse systemOne(const SystemOneRequest &request) const;
/**
* @brief Evaluates a SystemOne Request asynchronously.
* @returns A std::future containing the SystemOneResponse.
*/
std::future<SystemOneResponse> systemOneAsync(
const SystemOneRequest &request) const;
/**
* @brief Evaluates a SystemOne Request and parses the response into a
* custom model.
* @tparam T The custom model struct (must be compatible with
* nlohmann::json).
* @throws TypeSafeError on network, authentication, or API failure.
*/
template <typename T>
T systemOneAs(const SystemOneRequest &request) const
{
const HttpResponse res = executeSystemOne(request);
try
{
const auto res_json = nlohmann::json::parse(res.body);
return (res_json.get<T>());
}
catch (const nlohmann::json::exception &e)
{
throw ValidationError(std::string("Invalid JSON response: ")
+ e.what());
}
}
/**
* @brief Evaluates a SystemOne Request asynchronously into a custom
* model.
* @tparam T The custom model struct.
* @returns A std::future containing the parsed custom model.
*/
template <typename T>
std::future<T> systemOneAsAsync(const SystemOneRequest &request) const
{
auto config = _config;
auto transport = _transport;
return std::async(
std::launch::async,
[config = std::move(config), transport = std::move(transport),
request]() mutable
{
TypeSafeClient client(std::move(config),
std::move(transport));
return client.systemOneAs<T>(request);
});
}
/**
* @brief Lists available models.
* @throws TypeSafeError on network, authentication, or API failure.
*/
ListModelsResponse listModels() const;
/**
* @brief Lists available models asynchronously.
* @throws TypeSafeError on network, authentication, or API failure.
*/
std::future<ListModelsResponse> listModelsAsync() const;
private:
TypeSafeClientConfig _config;
std::shared_ptr<Transport> _transport;
TypeSafeClient(TypeSafeClientConfig config,
std::shared_ptr<Transport> transport);
HttpResponse executeSystemOne(const SystemOneRequest &request) const;
HttpResponse sendRequest(
const std::string &method,
const std::string &url,
const std::optional<std::string> &body,
int timeout_ms,
const std::map<std::string, std::string> &extra_headers = {}) const;
};
} // namespace typesafe