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 declarations
  • circuitry_helpers.c - Core implementations
  • circuitry_ui.h - UI function declarations
  • circuitry_ui.c - UI implementations
  • Makefile - 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

  1. Memory Management: Free every returned string; check for NULL first
  2. Error Checking: Helpers log failures with timestamps — check return values
  3. API Keys: Prefer environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY) over hardcoded keys
  4. Node.js: Install Node.js before running workflows with JavaScript code nodes
  5. Portability: Test on your target platforms; link libcurl everywhere