fix build failures: haskell streaming with withResponse, java-openai 4.3.0 with proper API, go-openai fixes, mojo real container

This commit is contained in:
Russell Ballestrini 2025-10-15 18:09:13 -04:00
parent d5c443fbdb
commit 60b61b5a7e
11 changed files with 91 additions and 159 deletions

View file

@ -4,8 +4,8 @@ FROM golang:1.22-alpine AS builder
WORKDIR /app
# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download
COPY go.mod ./
RUN go get github.com/openai/openai-go/v3@v3.3.0
# Copy source code
COPY main.go ./

View file

@ -2,7 +2,7 @@ module uncloseai-go-openai
go 1.22
require github.com/openai/openai-go v0.1.0-alpha.39
require github.com/openai/openai-go/v3 v3.3.0
require (
github.com/tidwall/gjson v1.14.4 // indirect

View file

@ -1,12 +0,0 @@
github.com/openai/openai-go v0.1.0-alpha.39 h1:FvoNWy7BPhA0TjGOK5huRGU5sAUEx2jeubLXz34K9LE=
github.com/openai/openai-go v0.1.0-alpha.39/go.mod h1:3SdE6BffOX9HPEQv8IL/fi3LYZ5TUpRYaqGQZbyk11A=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=

View file

@ -8,8 +8,8 @@ import (
"net/http"
"os"
"github.com/openai/openai-go"
"github.com/openai/openai-go/option"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/option"
)
type ModelsResponse struct {
@ -68,16 +68,17 @@ func main() {
// Non-streaming chat with Model 1
fmt.Println("=== Non-Streaming Chat (Model 1) ===")
// Use full endpoint including /v1 - v3 SDK doesn't add it
client1 := openai.NewClient(
option.WithBaseURL(modelEndpoint1),
option.WithAPIKey("dummy-key"),
)
chatResponse1, err := client1.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Model: openai.F(model1ID),
Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
Model: openai.ChatModel(model1ID),
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Give a Python Fizzbuzz solution in one line of code?"),
}),
},
Temperature: openai.Float(0.5),
MaxTokens: openai.Int(150),
})
@ -91,10 +92,10 @@ func main() {
// Streaming chat with Model 1
fmt.Println("=== Streaming Chat (Model 1) ===")
stream1 := client1.Chat.Completions.NewStreaming(context.Background(), openai.ChatCompletionNewParams{
Model: openai.F(model1ID),
Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
Model: openai.ChatModel(model1ID),
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Explain quantum entanglement in one sentence."),
}),
},
Temperature: openai.Float(0.5),
MaxTokens: openai.Int(150),
})
@ -114,16 +115,17 @@ func main() {
// Non-streaming chat with Model 2
fmt.Println("=== Non-Streaming Chat (Model 2) ===")
// Use full endpoint including /v1 - v3 SDK doesn't add it
client2 := openai.NewClient(
option.WithBaseURL(modelEndpoint2),
option.WithAPIKey("dummy-key"),
)
chatResponse2, err := client2.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
Model: openai.F(model2ID),
Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
Model: openai.ChatModel(model2ID),
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Write a JavaScript function to check if a number is prime"),
}),
},
Temperature: openai.Float(0.5),
MaxTokens: openai.Int(150),
})
@ -137,10 +139,10 @@ func main() {
// Streaming chat with Model 2
fmt.Println("=== Streaming Chat (Model 2) ===")
stream2 := client2.Chat.Completions.NewStreaming(context.Background(), openai.ChatCompletionNewParams{
Model: openai.F(model2ID),
Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
Model: openai.ChatModel(model2ID),
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Give a Python Fizzbuzz solution in one line of code?"),
}),
},
Temperature: openai.Float(0.5),
MaxTokens: openai.Int(150),
})
@ -160,15 +162,16 @@ func main() {
// TTS example
fmt.Println("=== TTS Speech Generation ===")
// Use full endpoint including /v1 - v3 SDK doesn't add it
ttsClient := openai.NewClient(
option.WithBaseURL(ttsEndpoint1),
option.WithAPIKey("dummy-key"),
)
response, err := ttsClient.Audio.Speech.New(context.Background(), openai.AudioSpeechNewParams{
Model: openai.F(openai.SpeechModelTTS1),
Voice: openai.F(openai.AudioSpeechNewParamsVoiceAlloy),
Input: openai.F("I think so therefore, Today is a wonderful day to grow something people love!"),
Model: openai.SpeechModelTTS1,
Voice: openai.AudioSpeechNewParamsVoiceAlloy,
Input: "I think so therefore, Today is a wonderful day to grow something people love!",
Speed: openai.Float(0.9),
})

View file

@ -259,8 +259,8 @@ chatStream client msgs maybeModelIdx maybeMaxToks maybeTemp = do
result <- try $ do
request <- parseRequest $ "POST " ++ modelEndpoint modelInfo ++ "/chat/completions"
let request' = setRequestBodyJSON req request
httpSink request' $ \response -> do
responseBody response
withResponse request' $ \response ->
runConduit $ getResponseBody response
.| CC.linesUnboundedAscii
.| CL.mapM_ processSSELine

View file

@ -18,7 +18,13 @@ FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# Copy jar with dependencies from builder
# Copy jar and dependencies from builder
COPY --from=builder /app/target/*.jar ./uncloseai.jar
COPY --from=builder /app/target/lib ./lib
CMD ["java", "-jar", "uncloseai.jar"]
# Set environment variables for testing
ENV MODEL_ENDPOINT_1=https://hermes.ai.unturf.com/v1
ENV MODEL_ENDPOINT_2=https://qwen.ai.unturf.com/v1
ENV TTS_ENDPOINT_1=https://speech.ai.unturf.com/v1
CMD ["java", "-cp", "uncloseai.jar:lib/*", "uncloseai"]

View file

@ -18,7 +18,7 @@
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java</artifactId>
<version>0.8.1</version>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
@ -59,6 +59,18 @@
<mainClass>uncloseai</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>uncloseai</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,6 +1,7 @@
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.*;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import java.io.FileOutputStream;
import java.io.IOException;
@ -8,8 +9,6 @@ import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.stream.Stream;
import org.json.JSONArray;
import org.json.JSONObject;
@ -65,14 +64,7 @@ public class uncloseai {
ChatCompletionCreateParams params1 = ChatCompletionCreateParams.builder()
.model(model1Id)
.addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
ChatCompletionUserMessageParam.builder()
.role(ChatCompletionUserMessageParam.Role.USER)
.content(ChatCompletionUserMessageParam.Content.ofTextContent(
"Give a Python Fizzbuzz solution in one line of code?"
))
.build()
))
.addUserMessage("Give a Python Fizzbuzz solution in one line of code?")
.temperature(0.5)
.maxTokens(150L)
.build();
@ -84,14 +76,7 @@ public class uncloseai {
System.out.println("=== Streaming Chat (Model 1) ===");
ChatCompletionCreateParams streamParams1 = ChatCompletionCreateParams.builder()
.model(model1Id)
.addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
ChatCompletionUserMessageParam.builder()
.role(ChatCompletionUserMessageParam.Role.USER)
.content(ChatCompletionUserMessageParam.Content.ofTextContent(
"Explain quantum entanglement in one sentence."
))
.build()
))
.addUserMessage("Explain quantum entanglement in one sentence.")
.temperature(0.5)
.maxTokens(150L)
.build();
@ -113,14 +98,7 @@ public class uncloseai {
ChatCompletionCreateParams params2 = ChatCompletionCreateParams.builder()
.model(model2Id)
.addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
ChatCompletionUserMessageParam.builder()
.role(ChatCompletionUserMessageParam.Role.USER)
.content(ChatCompletionUserMessageParam.Content.ofTextContent(
"Write a JavaScript function to check if a number is prime"
))
.build()
))
.addUserMessage("Write a JavaScript function to check if a number is prime")
.temperature(0.5)
.maxTokens(150L)
.build();
@ -132,14 +110,7 @@ public class uncloseai {
System.out.println("=== Streaming Chat (Model 2) ===");
ChatCompletionCreateParams streamParams2 = ChatCompletionCreateParams.builder()
.model(model2Id)
.addMessage(ChatCompletionMessageParam.ofChatCompletionUserMessageParam(
ChatCompletionUserMessageParam.builder()
.role(ChatCompletionUserMessageParam.Role.USER)
.content(ChatCompletionUserMessageParam.Content.ofTextContent(
"Give a Python Fizzbuzz solution in one line of code?"
))
.build()
))
.addUserMessage("Give a Python Fizzbuzz solution in one line of code?")
.temperature(0.5)
.maxTokens(150L)
.build();
@ -152,9 +123,9 @@ public class uncloseai {
});
System.out.println("\n");
// TTS example - not supported in openai-java 0.8.1
// TTS example
System.out.println("=== TTS Speech Generation ===");
System.out.println("[SKIPPED] TTS not available in openai-java SDK 0.8.1\n");
System.out.println("[SKIPPED] TTS implementation needs audio API research\n");
System.out.println("=== Examples Complete ===");

View file

@ -22,6 +22,6 @@
"node": ">=18.0.0"
},
"dependencies": {
"openai": "^4.77.0"
"openai": "6.3.0"
}
}

View file

@ -1,18 +1,44 @@
# Mojo examples (source code reference)
# Note: Mojo SDK requires Modular license which isn't freely available in Docker
# We provide Python equivalents that demonstrate the same patterns
FROM python:3.13-alpine
# Mojo language container built from Ubuntu (Modular supports Ubuntu/Debian)
FROM ubuntu:24.04
WORKDIR /app
# Install HTTP client
RUN pip install --no-cache-dir httpx==0.28.1
# Install dependencies
RUN apt-get update && apt-get install -y \
curl \
ca-certificates \
python3 \
python3-pip \
python3-venv \
&& rm -rf /var/lib/apt/lists/*
# Copy source files
# Create virtual environment for pip packages
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install Modular CLI
RUN curl -s https://get.modular.com | sh -
# Add modular to PATH
ENV PATH="/root/.modular/bin:$PATH"
# Copy Mojo source files
COPY hermes_nonstreaming.mojo .
COPY qwen_nonstreaming.mojo .
COPY uncloseai.py .
COPY README.md .
# Default command shows README and runs Python equivalent
CMD ["sh", "-c", "echo '=== Mojo Examples (Reference) ===\n' && cat README.md && echo '\n\n=== Running Python Equivalent ===\n' && python uncloseai.py"]
# Create entrypoint script
RUN echo '#!/bin/bash\n\
if [ -z "$MODULAR_AUTH" ]; then\n\
echo "ERROR: MODULAR_AUTH environment variable not set"\n\
echo "Get your auth key from: https://developer.modular.com/"\n\
echo "Then run: docker run -e MODULAR_AUTH=your-key ai-unturf-mojo"\n\
exit 1\n\
fi\n\
modular auth $MODULAR_AUTH\n\
modular install mojo\n\
magic run mojo hermes_nonstreaming.mojo\n\
' > /app/run.sh && chmod +x /app/run.sh
# Default: run Mojo example
CMD ["/app/run.sh"]

View file

@ -1,74 +0,0 @@
#!/usr/bin/env python3
# uncloseai. - Python equivalent demonstrating patterns shown in Mojo examples
# This runs in place of Mojo since Mojo SDK requires Modular license
import os
import httpx
import json
print("=== uncloseai. Mojo Examples (Python Equivalent) ===\n")
# Discover endpoints from environment variables
model_endpoint_1 = os.getenv("MODEL_ENDPOINT_1")
model_endpoint_2 = os.getenv("MODEL_ENDPOINT_2")
tts_endpoint_1 = os.getenv("TTS_ENDPOINT_1")
if not model_endpoint_1 or not model_endpoint_2 or not tts_endpoint_1:
print("ERROR: No models discovered. Set environment variables:")
print(" MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, TTS_ENDPOINT_1")
exit(1)
# Discover models from endpoint 1
print(f"Discovering models from {model_endpoint_1}...")
response1 = httpx.get(f"{model_endpoint_1}/models")
models1 = response1.json()["data"]
model_1_id = models1[0]["id"]
print(f"Model 1: {model_1_id}\n")
# Discover models from endpoint 2
print(f"Discovering models from {model_endpoint_2}...")
response2 = httpx.get(f"{model_endpoint_2}/models")
models2 = response2.json()["data"]
model_2_id = models2[0]["id"]
print(f"Model 2: {model_2_id}\n")
# Non-streaming chat with Model 1
print("=== Non-Streaming Chat (Model 1) ===")
payload1 = {
"model": model_1_id,
"messages": [{"role": "user", "content": "Give a Python Fizzbuzz solution in one line of code?"}],
"temperature": 0.5,
"max_tokens": 150
}
chat_response1 = httpx.post(f"{model_endpoint_1}/chat/completions", json=payload1)
content1 = chat_response1.json()["choices"][0]["message"]["content"]
print(f"Response: {content1}\n")
# Non-streaming chat with Model 2
print("=== Non-Streaming Chat (Model 2) ===")
payload2 = {
"model": model_2_id,
"messages": [{"role": "user", "content": "Give a Python Fizzbuzz solution in one line of code?"}],
"temperature": 0.5,
"max_tokens": 150
}
chat_response2 = httpx.post(f"{model_endpoint_2}/chat/completions", json=payload2)
content2 = chat_response2.json()["choices"][0]["message"]["content"]
print(f"Response: {content2}\n")
# TTS example
print("=== TTS Speech Generation ===")
tts_payload = {
"model": "tts-1",
"voice": "alloy",
"input": "I think so therefore, Today is a wonderful day to grow something people love!",
"speed": 0.9
}
tts_response = httpx.post(f"{tts_endpoint_1}/audio/speech", json=tts_payload)
with open("speech.mp3", "wb") as f:
f.write(tts_response.content)
print(f"[OK] Speech file created: speech.mp3 ({len(tts_response.content)} bytes)\n")
print("=== Examples Complete ===")
print("\nNote: The .mojo files in this directory show what this would look like")
print("in Mojo. We run Python equivalents since Mojo SDK requires Modular license.")