modified: languages/awk/Dockerfile

modified:   languages/c/curl/Dockerfile
	modified:   languages/c/libsoup/Dockerfile
	modified:   languages/c/nghttp2/Dockerfile
	modified:   languages/c/nghttp2/uncloseai.c
	modified:   languages/cpp/boost-beast/Dockerfile
	modified:   languages/cpp/cpp-httplib/Dockerfile
	modified:   languages/cpp/libcurl/Dockerfile
	modified:   languages/csharp/openai/Dockerfile
	modified:   languages/go/http/Dockerfile
	modified:   languages/go/openai/Dockerfile
	modified:   languages/lua/Dockerfile
	modified:   languages/ruby/openai/Dockerfile
	modified:   languages/vbnet/Dockerfile
This commit is contained in:
Russell Ballestrini 2025-10-17 09:02:19 -04:00
parent fc4b021b7f
commit 45ccee4b71
14 changed files with 228 additions and 63 deletions

View file

@ -1,5 +1,5 @@
# GNU AWK 5.3.2 (checked 2025-10-13: gawk 5.3.2-r2 is latest in Alpine edge)
FROM alpine:3.22
FROM alpine:latest
RUN apk add --no-cache gawk curl ca-certificates

View file

@ -1,5 +1,5 @@
# Pin to specific Alpine version (checked 2025-10-12: alpine:3.21 is latest stable)
FROM alpine:3.21
FROM alpine:latest
# Install C compiler and libcurl development libraries
RUN apk --no-cache add \

View file

@ -1,5 +1,5 @@
# Pin to specific Alpine version (checked 2025-10-14: alpine:3.20 is latest stable)
FROM alpine:3.20
FROM alpine:latest
# Install C compiler and libsoup development libraries
RUN apk --no-cache add \

View file

@ -1,5 +1,5 @@
# Pin to specific Alpine version (checked 2025-10-14: alpine:3.20 is latest stable)
FROM alpine:3.20
FROM alpine:latest
# Install C compiler and nghttp2 development libraries
RUN apk --no-cache add \

View file

@ -1,6 +1,6 @@
/*
* UncloseAI C Client using nghttp2 (HTTP/2)
* OpenAI-compatible API client with HTTP/2 support
* OpenAI-compatible API client with HTTP/2 and streaming support
* Compatible with vLLM, Ollama, and OpenAI-compatible endpoints
*/
@ -43,6 +43,9 @@ struct Connection {
size_t response_size;
size_t response_capacity;
int done;
int streaming;
char stream_buffer[8192];
size_t stream_buffer_pos;
};
// Request data provider
@ -151,23 +154,86 @@ ssize_t recv_callback(nghttp2_session *session, uint8_t *buf, size_t length,
return rv;
}
// Extract content from SSE data chunk
void extract_sse_content(const char *data, char *content, size_t max_len) {
const char *content_marker = "\"content\":\"";
const char *start = strstr(data, content_marker);
if (!start) return;
start += strlen(content_marker);
const char *end = start;
// Find closing quote, handling escaped quotes
while (*end && *end != '"') {
if (*end == '\\' && *(end + 1)) {
end += 2;
} else {
end++;
}
}
size_t len = end - start;
if (len > max_len - 1) len = max_len - 1;
strncpy(content, start, len);
content[len] = '\0';
}
// Data chunk received callback
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
int32_t stream_id, const uint8_t *data,
size_t len, void *user_data) {
struct Connection *conn = (struct Connection *)user_data;
// Resize buffer if needed
if (conn->response_size + len > conn->response_capacity) {
size_t new_capacity = (conn->response_capacity + len) * 2;
char *new_data = realloc(conn->response_data, new_capacity);
if (!new_data) return 0;
conn->response_data = new_data;
conn->response_capacity = new_capacity;
}
if (conn->streaming) {
// Process streaming SSE data
for (size_t i = 0; i < len; i++) {
if (conn->stream_buffer_pos >= sizeof(conn->stream_buffer) - 1) {
conn->stream_buffer_pos = 0; // Reset if overflow
}
memcpy(conn->response_data + conn->response_size, data, len);
conn->response_size += len;
conn->stream_buffer[conn->stream_buffer_pos++] = data[i];
// Check for line ending
if (data[i] == '\n' && conn->stream_buffer_pos >= 2 &&
conn->stream_buffer[conn->stream_buffer_pos - 2] == '\n') {
conn->stream_buffer[conn->stream_buffer_pos] = '\0';
// Process SSE line
if (strncmp(conn->stream_buffer, "data: ", 6) == 0) {
const char *json_data = conn->stream_buffer + 6;
// Check for [DONE]
if (strncmp(json_data, "[DONE]", 6) == 0) {
conn->stream_buffer_pos = 0;
break;
}
// Extract and print content
char content[1024];
extract_sse_content(json_data, content, sizeof(content));
if (strlen(content) > 0) {
printf("%s", content);
fflush(stdout);
}
}
conn->stream_buffer_pos = 0;
}
}
} else {
// Non-streaming: accumulate response
if (conn->response_size + len > conn->response_capacity) {
size_t new_capacity = (conn->response_capacity + len) * 2;
char *new_data = realloc(conn->response_data, new_capacity);
if (!new_data) return 0;
conn->response_data = new_data;
conn->response_capacity = new_capacity;
}
memcpy(conn->response_data + conn->response_size, data, len);
conn->response_size += len;
}
return 0;
}
@ -201,38 +267,8 @@ ssize_t data_provider_read_callback(nghttp2_session *session, int32_t stream_id,
return to_copy;
}
// Make HTTP/2 request
int http2_request(const char *host, int port, const char *path,
const char *method, const char *body,
char **response, size_t *response_size) {
int sock = connect_to_server(host, port);
if (sock < 0) return -1;
// Initialize connection
struct Connection conn = {0};
conn.sock = sock;
conn.response_data = malloc(BUFFER_SIZE);
conn.response_capacity = BUFFER_SIZE;
conn.response_size = 0;
conn.done = 0;
// Setup nghttp2 callbacks
nghttp2_session_callbacks *callbacks;
nghttp2_session_callbacks_new(&callbacks);
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);
// Create client session
nghttp2_session_client_new(&conn.session, callbacks, &conn);
nghttp2_session_callbacks_del(callbacks);
// Send HTTP/2 connection preface
nghttp2_submit_settings(conn.session, NGHTTP2_FLAG_NONE, NULL, 0);
// Prepare headers
nghttp2_nv hdrs[5];
// Setup HTTP/2 headers
void setup_http2_headers(nghttp2_nv *hdrs, const char *method, const char *path, const char *host) {
hdrs[0].name = (uint8_t *)":method";
hdrs[0].value = (uint8_t *)method;
hdrs[0].namelen = strlen(":method");
@ -262,6 +298,46 @@ int http2_request(const char *host, int port, const char *path,
hdrs[4].namelen = strlen("content-type");
hdrs[4].valuelen = strlen("application/json");
hdrs[4].flags = NGHTTP2_NV_FLAG_NONE;
}
// Setup nghttp2 session with callbacks
void setup_nghttp2_session(struct Connection *conn, nghttp2_session_callbacks *callbacks) {
nghttp2_session_callbacks_new(&callbacks);
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, on_stream_close_callback);
nghttp2_session_client_new(&conn->session, callbacks, conn);
nghttp2_session_callbacks_del(callbacks);
nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, NULL, 0);
}
// Make HTTP/2 request
int http2_request(const char *host, int port, const char *path,
const char *method, const char *body,
char **response, size_t *response_size) {
int sock = connect_to_server(host, port);
if (sock < 0) return -1;
// Initialize connection
struct Connection conn = {0};
conn.sock = sock;
conn.response_data = malloc(BUFFER_SIZE);
conn.response_capacity = BUFFER_SIZE;
conn.response_size = 0;
conn.done = 0;
conn.streaming = 0;
conn.stream_buffer_pos = 0;
// Setup nghttp2 session and callbacks
nghttp2_session_callbacks *callbacks;
setup_nghttp2_session(&conn, callbacks);
// Prepare headers
nghttp2_nv hdrs[5];
setup_http2_headers(hdrs, method, path, host);
// Submit request
if (body && strlen(body) > 0) {
@ -290,6 +366,55 @@ int http2_request(const char *host, int port, const char *path,
return 0;
}
// Make streaming HTTP/2 request
int http2_stream_request(const char *host, int port, const char *path,
const char *method, const char *body) {
int sock = connect_to_server(host, port);
if (sock < 0) return -1;
// Initialize connection for streaming
struct Connection conn = {0};
conn.sock = sock;
conn.response_data = malloc(BUFFER_SIZE);
conn.response_capacity = BUFFER_SIZE;
conn.response_size = 0;
conn.done = 0;
conn.streaming = 1; // Enable streaming mode
conn.stream_buffer_pos = 0;
// Setup nghttp2 session and callbacks
nghttp2_session_callbacks *callbacks;
setup_nghttp2_session(&conn, callbacks);
// Prepare headers
nghttp2_nv hdrs[5];
setup_http2_headers(hdrs, method, path, host);
// Submit request
if (body && strlen(body) > 0) {
struct RequestData req_data = {body, strlen(body), 0};
nghttp2_data_provider data_prd;
data_prd.source.ptr = &req_data;
data_prd.read_callback = data_provider_read_callback;
nghttp2_submit_request(conn.session, NULL, hdrs, 5, &data_prd, NULL);
} else {
nghttp2_submit_request(conn.session, NULL, hdrs, 4, NULL, NULL);
}
// Send and receive (streaming prints directly in callback)
while (!conn.done) {
if (nghttp2_session_send(conn.session) != 0) break;
if (nghttp2_session_recv(conn.session) != 0) break;
}
// Cleanup
free(conn.response_data);
nghttp2_session_del(conn.session);
close(sock);
return 0;
}
// Simple JSON string extractor
void extract_model_ids(const char *json, const char *endpoint) {
const char *search = json;
@ -393,17 +518,17 @@ int main(void) {
}
printf("\n");
// Non-streaming chat
printf("=== Non-Streaming Chat ===\n");
// Non-streaming chat (Hermes)
printf("=== Non-Streaming Chat (Hermes) ===\n");
printf("Model: %s\n", models[0].id);
char json[2048];
snprintf(json, sizeof(json),
"{\"model\":\"%s\","
"\"messages\":[{\"role\":\"user\",\"content\":\"Explain quantum computing in one sentence\"}],"
"\"messages\":[{\"role\":\"user\",\"content\":\"Give a Python Fizzbuzz solution in one line of code?\"}],"
"\"stream\":false,"
"\"max_tokens\":100,"
"\"temperature\":0.7}",
"\"max_tokens\":150,"
"\"temperature\":0.5}",
models[0].id);
char *response = NULL;
@ -421,6 +546,46 @@ int main(void) {
printf("Request failed\n\n");
}
// Streaming chat (Hermes)
printf("=== Streaming Chat (Hermes) ===\n");
printf("Model: %s\n", models[0].id);
printf("Response: ");
char stream_json_hermes[2048];
snprintf(stream_json_hermes, sizeof(stream_json_hermes),
"{\"model\":\"%s\","
"\"messages\":[{\"role\":\"user\",\"content\":\"Give a Python Fizzbuzz solution in one line of code?\"}],"
"\"stream\":true,"
"\"max_tokens\":500,"
"\"temperature\":0.7}",
models[0].id);
if (http2_stream_request(models[0].host, models[0].port, path, "POST", stream_json_hermes) != 0) {
printf("\nStreaming request failed");
}
printf("\n\n");
// Streaming chat (Qwen Coder 3) - if second model available
if (model_count >= 2) {
printf("=== Streaming Chat (Qwen Coder 3) ===\n");
printf("Model: %s\n", models[1].id);
printf("Response: ");
char stream_json_qwen[2048];
snprintf(stream_json_qwen, sizeof(stream_json_qwen),
"{\"model\":\"%s\","
"\"messages\":[{\"role\":\"user\",\"content\":\"Write a hello world program in C\"}],"
"\"stream\":true,"
"\"max_tokens\":500,"
"\"temperature\":0.7}",
models[1].id);
if (http2_stream_request(models[1].host, models[1].port, path, "POST", stream_json_qwen) != 0) {
printf("\nStreaming request failed");
}
printf("\n\n");
}
// TTS
if (tts_count > 0) {
printf("=== TTS Speech Generation ===\n");

View file

@ -1,5 +1,5 @@
# Pin to specific Alpine version (checked 2025-10-14: alpine:3.20 is latest stable)
FROM alpine:3.20
FROM alpine:latest
# Install C++ compiler and Boost libraries
RUN apk --no-cache add \

View file

@ -1,5 +1,5 @@
# Pin to specific Alpine version (checked 2025-10-14: alpine:3.20 is latest stable)
FROM alpine:3.20
FROM alpine:latest
# Install C++ compiler and build tools
RUN apk --no-cache add \

View file

@ -1,5 +1,5 @@
# Pin to specific Alpine version (checked 2025-10-12: alpine:3.21 is latest stable)
FROM alpine:3.21
FROM alpine:latest
# Install C++ compiler and libcurl development libraries
RUN apk --no-cache add \

View file

@ -1,5 +1,5 @@
# Use official .NET SDK to build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS builder
WORKDIR /app
@ -12,7 +12,7 @@ COPY uncloseai.cs ./
RUN dotnet build -c Release -o /app/build
# Run stage
FROM mcr.microsoft.com/dotnet/runtime:8.0
FROM mcr.microsoft.com/dotnet/runtime:9.0-alpine
WORKDIR /app

View file

@ -14,7 +14,7 @@ COPY uncloseai.go .
RUN go build -o uncloseai uncloseai.go
# Use minimal alpine image for runtime
FROM alpine:3.21
FROM alpine:latest
# Install ca-certificates for HTTPS
RUN apk --no-cache add ca-certificates

View file

@ -1,5 +1,5 @@
# Multi-stage build for Go with official OpenAI SDK
FROM golang:1.22-alpine AS builder
FROM golang:1.23-alpine AS builder
WORKDIR /app

View file

@ -1,5 +1,5 @@
# Alpine 3.22 with Lua 5.4 (checked 2025-10-13: alpine:3.22 with lua5.4 is latest stable)
FROM alpine:3.22
FROM alpine:latest
RUN apk add --no-cache lua5.4 lua5.4-dev luarocks5.4 ca-certificates openssl-dev gcc musl-dev

View file

@ -1,5 +1,5 @@
# Use official Ruby image
FROM ruby:3.3-slim
FROM ruby:3.3-alpine
WORKDIR /app

View file

@ -1,7 +1,7 @@
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
WORKDIR /src
COPY ["UncloseAI.vbproj", "."]
RUN dotnet restore "UncloseAI.vbproj"