| 198 | } |
| 199 | |
| 200 | bool Tracer::send_batch(const JsonValue& events) { |
| 201 | if (!is_valid()) { |
| 202 | ai::logger::log_warn( |
| 203 | "Langfuse tracer not configured (missing host/public_key/secret_key); " |
| 204 | "dropping {} events", |
| 205 | events.is_array() ? events.size() : 0); |
| 206 | return false; |
| 207 | } |
| 208 | |
| 209 | JsonValue body; |
| 210 | body["batch"] = events; |
| 211 | std::string serialized = body.dump(); |
| 212 | |
| 213 | std::lock_guard<std::mutex> lock(mu_); |
| 214 | auto& s = http_state(); |
| 215 | std::string path = s.base_path + kIngestionPath; |
| 216 | auto res = |
| 217 | s.client.Post(path.c_str(), s.headers, serialized, "application/json"); |
| 218 | |
| 219 | if (!res) { |
| 220 | ai::logger::log_error("Langfuse ingestion failed: {}", |
| 221 | httplib::to_string(res.error())); |
| 222 | return false; |
| 223 | } |
| 224 | if (res->status >= 200 && res->status < 300) { |
| 225 | ai::logger::log_debug("Langfuse ingestion accepted ({}): {}", res->status, |
| 226 | res->body); |
| 227 | return true; |
| 228 | } |
| 229 | ai::logger::log_error("Langfuse ingestion non-2xx ({}): {}", res->status, |
| 230 | res->body); |
| 231 | return false; |
| 232 | } |
| 233 | |
| 234 | // --------------------------------------------------------------------------- |
| 235 | // Trace |