C Code Generation
Generate portable C code with a compact helper library for HTTP, AI calls, JSON handling, and template variables.
Overview
Circuitry generates C code that:
- Follows the C11 standard
- Works on macOS, Linux, and Windows
- Uses libcurl for HTTP and AI requests
- Includes a lightweight string-based JSON helper set
- Can run JavaScript code nodes through Node.js
Requirements
- A C compiler (clang, gcc, or MSVC)
- libcurl — preinstalled on macOS; on Linux install
libcurl4-openssl-dev(or your distro's equivalent); on Windows use a libcurl build (e.g. via vcpkg) - Node.js on your PATH if the workflow uses JavaScript code nodes (
execute_javascript)
Generated Code Structure
#include <stdio.h>
#include <stdlib.h>
#include "circuitry_helpers.h"
#include "circuitry_ui.h"
char* workflow_name(const char* input_json) {
// HTTP Request — returns the response body (caller frees)
char* body = http_request("https://api.example.com/data", "GET", NULL, NULL);
if (!body) return NULL;
// Extract a value and build the next node's input
char* name = json_get_field(body, "user.name");
char* data = json_set_field("{}", "name", "\"unknown\"");
if (name) {
char* escaped = json_escape_string(name);
char* quoted = malloc(strlen(escaped) + 3);
sprintf(quoted, "\"%s\"", escaped);
char* updated = json_set_field(data, "name", quoted);
free(data); data = updated;
free(quoted); free(escaped); free(name);
}
free(body);
return data; // JSON string result
}
Helper Libraries
Installation
Download the helper libraries from the Code Generation dialog:
circuitry_helpers.h- Core function declarationscircuitry_helpers.c- Core implementationscircuitry_ui.h- UI function declarationscircuitry_ui.c- UI implementationsMakefile- Build configuration
Building
# Using Make
make
# Manual compilation (macOS / Linux)
cc -o workflow workflow.c circuitry_helpers.c circuitry_ui.c -lcurl
Core Functions
HTTP Requests
http_request(url, method, body, headers) returns the response body as a
heap-allocated string (free it when done), or NULL on connection failure.
// Simple GET request
char* body = http_request("https://api.example.com", "GET", NULL, NULL);
// POST with body and headers (headers are newline-separated)
const char* payload = "{\"key\": \"value\"}";
const char* headers = "Content-Type: application/json\nAuthorization: Bearer token";
char* response = http_request(url, "POST", payload, headers);
// Clean up
free(body);
free(response);
AI Calls
call_ai(prompt, options) is the main entry point. Pass NULL for defaults,
or a zero-initialized AIOptions struct with the fields you need. The API
key falls back to the OPENAI_API_KEY / ANTHROPIC_API_KEY environment
variables; models containing "claude" are routed to the Anthropic API
automatically, and setting endpoint targets any OpenAI-compatible server.
// Simplest form — uses defaults + OPENAI_API_KEY from the environment
char* reply = call_ai("Summarize this in one sentence: ...", NULL);
// With options
AIOptions opts = {0};
opts.model = "claude-opus-4-8"; // "claude" in the name -> Anthropic API
opts.system_prompt = "You are a helpful assistant.";
opts.max_tokens = 2048; // <= 0 uses the default (1024)
opts.temperature = 0.7; // <= 0 omits the field
char* answer = call_ai("Explain recursion briefly.", &opts);
printf("%s\n", answer);
free(reply);
free(answer);
Convenience wrappers:
// Anthropic models (api_key NULL -> ANTHROPIC_API_KEY env var)
char* a = call_claude("Hello!", "claude-opus-4-8", NULL, 0, 1024);
// Local / self-hosted OpenAI-compatible endpoint
char* b = call_local_llm("Hello!", "http://localhost:11434/v1/chat/completions",
"llama3", 0, 1024);
free(a);
free(b);
JavaScript Code Nodes
execute_javascript(code, data_json) runs a JavaScript code node with
Node.js (which must be installed). The code receives a data variable
parsed from data_json; its return value (or the mutated data object)
comes back as a JSON string. console.log output inside the code is safe —
it does not corrupt the result.
char* result = execute_javascript(
"return { total: data.a + data.b };",
"{\"a\": 2, \"b\": 3}");
printf("%s\n", result); // {"total":5}
free(result);
JSON Handling
// First occurrence of a field, anywhere in the document
char* value = parse_json_field("{\"name\": \"John\"}", "name");
printf("Name: %s\n", value);
free(value);
// Dotted-path lookup
char* city = json_get_field("{\"user\": {\"city\": \"Oslo\"}}", "user.city");
free(city);
// Set or replace a top-level key (value is raw JSON)
char* updated = json_set_field("{\"a\": 1}", "b", "\"hello\"");
// updated = {"a": 1,"b":"hello"}
free(updated);
// Escape text for embedding in JSON
char* safe = json_escape_string("line1\nline2 \"quoted\"");
free(safe);
// Create a flat string-valued JSON object
const char* pairs[] = {"name", "John", "age", "30"};
char* json = create_json(pairs, 2);
printf("JSON: %s\n", json); // {"name":"John","age":"30"}
free(json);
Template Variables
{{input}} and {{previousOutput}} resolve to the whole data object;
dotted paths like {{input.user.name}} or {{user.name}} resolve against
the data. Unknown placeholders become empty strings.
const char* tpl = "Hello {{name}}, your city is {{address.city}}";
const char* data = "{\"name\": \"Alice\", \"address\": {\"city\": \"Oslo\"}}";
char* result = replace_template_variables(tpl, data);
printf("%s\n", result); // "Hello Alice, your city is Oslo"
free(result);
UI Functions
Confirmations
ConfirmResult* result = confirm_action("Process order?", "Order Confirmation");
if (result->confirmed) {
printf("User confirmed at %s\n", result->timestamp);
}
free_confirm_result(result);
Messages
MessageResult* msg = show_message("Operation complete!", "success", "Success");
if (msg->acknowledged) {
printf("Message shown: %s\n", msg->message);
}
free_message_result(msg);
Form Input
FormField fields[] = {
{"email", "Email Address", "text", true, NULL},
{"age", "Age", "number", false, "18"}
};
FormResult* form = get_form_input(fields, 2, "User Registration");
if (form->submitted) {
for (int i = 0; i < form->field_count; i++) {
printf("%s: %s\n", form->field_names[i], form->field_values[i]);
}
}
free_form_result(form);
Memory Management
Every helper that returns char* returns a heap-allocated string you must
free(). NULL always means failure.
char* body = http_request(url, "GET", NULL, NULL);
if (body != NULL) {
// use body
free(body);
}
// WorkflowData helpers
WorkflowData* copy = clone_workflow_data(original);
// ... modify copy without affecting original
free_workflow_data(copy);
Error Handling
char* reply = call_ai(prompt, &opts);
if (reply == NULL) {
// Details were logged with a timestamp; decide whether to retry
log_message("ERROR", "AI call failed");
return NULL;
}
Retry helper:
void* fetch(void* url) {
return http_request((const char*)url, "GET", NULL, NULL);
}
char* body = execute_with_retry(fetch, (void*)"https://api.example.com",
3, // max retries
1000); // delay between retries (ms)
Cross-Platform Compilation
Linux
sudo apt-get install libcurl4-openssl-dev
cc -o workflow workflow.c circuitry_helpers.c circuitry_ui.c -lcurl
macOS
# libcurl ships with macOS
cc -o workflow workflow.c circuitry_helpers.c circuitry_ui.c -lcurl
Windows
REM Install libcurl first, e.g.: vcpkg install curl
cl /Fe:workflow.exe workflow.c circuitry_helpers.c circuitry_ui.c libcurl.lib
Testing
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "circuitry_helpers.h"
void test_json(void) {
char* name = parse_json_field("{\"name\": \"test\", \"value\": 42}", "name");
assert(strcmp(name, "test") == 0);
free(name);
char* nested = json_get_field("{\"a\": {\"b\": \"deep\"}}", "a.b");
assert(strcmp(nested, "deep") == 0);
free(nested);
}
void test_templates(void) {
char* result = replace_template_variables("Hello {{name}}", "{\"name\": \"World\"}");
assert(strcmp(result, "Hello World") == 0);
free(result);
}
int main(void) {
test_json();
test_templates();
printf("All tests passed!\n");
return 0;
}
Best Practices
- Memory Management: Free every returned string; check for NULL first
- Error Checking: Helpers log failures with timestamps — check return values
- API Keys: Prefer environment variables (
OPENAI_API_KEY,ANTHROPIC_API_KEY) over hardcoded keys - Node.js: Install Node.js before running workflows with JavaScript code nodes
- Portability: Test on your target platforms; link libcurl everywhere