Skip to content

Commit 4b7bafa

Browse files
Replace OpenAI API with local mock server for testing
- Add mock-openai-server.py that returns canned responses for chat completions - Update Rust/Go/JS OpenAI activities to support OPENAI_API_BASE_URL env var - Update obelisk config files to pass OPENAI_API_BASE_URL to activities - Update test-e2e.sh and test-integration.sh to start mock server - Remove E2E_OPENAI_API_KEY and TEST_OPENAI_API_KEY from GitHub workflows This eliminates the need for paid OpenAI API calls during CI testing. Co-authored-by: Shelley <shelley@exe.dev>
1 parent f3598fa commit 4b7bafa

18 files changed

Lines changed: 164 additions & 26 deletions

.github/workflows/check-test-e2e.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ jobs:
4646

4747
- name: Verify obelisk-local.toml
4848
env:
49-
OPENAI_API_KEY: ${{ secrets.E2E_OPENAI_API_KEY }}
5049
GITHUB_TOKEN: ${{ secrets.E2E_GITHUB_TOKEN }}
5150
TEST_GITHUB_LOGIN: ${{ secrets.TEST_GITHUB_LOGIN }}
5251
TURSO_TOKEN: ${{ secrets.E2E_TURSO_TOKEN }}
@@ -56,7 +55,6 @@ jobs:
5655
5756
- name: Verify obelisk-oci.toml
5857
env:
59-
OPENAI_API_KEY: ${{ secrets.E2E_OPENAI_API_KEY }}
6058
GITHUB_TOKEN: ${{ secrets.E2E_GITHUB_TOKEN }}
6159
TEST_GITHUB_LOGIN: ${{ secrets.TEST_GITHUB_LOGIN }}
6260
TURSO_TOKEN: ${{ secrets.E2E_TURSO_TOKEN }}
@@ -69,7 +67,6 @@ jobs:
6967

7068
- name: Verify obelisk-local-js-all.toml
7169
env:
72-
OPENAI_API_KEY: ${{ secrets.E2E_OPENAI_API_KEY }}
7370
GITHUB_TOKEN: ${{ secrets.E2E_GITHUB_TOKEN }}
7471
TEST_GITHUB_LOGIN: ${{ secrets.TEST_GITHUB_LOGIN }}
7572
TURSO_TOKEN: ${{ secrets.E2E_TURSO_TOKEN }}
@@ -82,7 +79,6 @@ jobs:
8279

8380
- name: Verify obelisk-local-go-all.toml
8481
env:
85-
OPENAI_API_KEY: ${{ secrets.E2E_OPENAI_API_KEY }}
8682
GITHUB_TOKEN: ${{ secrets.E2E_GITHUB_TOKEN }}
8783
TEST_GITHUB_LOGIN: ${{ secrets.TEST_GITHUB_LOGIN }}
8884
TURSO_TOKEN: ${{ secrets.E2E_TURSO_TOKEN }}

.github/workflows/check-test-integration.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,9 @@ jobs:
4444
- name: Run integration tests
4545
env:
4646
CI: true
47-
TEST_OPENAI_API_KEY: ${{ secrets.TEST_OPENAI_API_KEY }}
4847
TEST_GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }}
4948
TEST_GITHUB_LOGIN: ${{ secrets.TEST_GITHUB_LOGIN }}
5049
TEST_GITHUB_REPO: ${{ secrets.TEST_GITHUB_REPO }}
5150
TEST_TURSO_TOKEN: ${{ secrets.TEST_TURSO_TOKEN }}
5251
TEST_TURSO_LOCATION: ${{ secrets.TEST_TURSO_LOCATION }}
53-
5452
run: nix develop --command ./scripts/test-integration.sh

activity/llm/openai-go/main.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
)
1616

1717
const openAIEnv = "OPENAI_API_KEY"
18+
const openAIBaseURLEnv = "OPENAI_API_BASE_URL"
19+
const defaultOpenAIBaseURL = "https://api.openai.com"
1820

1921

2022
type Role string
@@ -71,6 +73,12 @@ func respond(userPrompt string, settingsJSON string) (result cm.Result[string, s
7173
)
7274
}
7375

76+
// Base URL (optional, defaults to api.openai.com)
77+
baseURL := os.Getenv(openAIBaseURLEnv)
78+
if baseURL == "" {
79+
baseURL = defaultOpenAIBaseURL
80+
}
81+
7482
// 2. Parse settings
7583
var settings Settings
7684
if err := json.Unmarshal([]byte(settingsJSON), &settings); err != nil {
@@ -100,7 +108,7 @@ func respond(userPrompt string, settingsJSON string) (result cm.Result[string, s
100108
fmt.Println("OpenAI request:", string(rawReq))
101109

102110
// 5. Do HTTP POST via wasihttp
103-
req, err := http.NewRequest(http.MethodPost, "https://api.openai.com/v1/chat/completions", bytes.NewReader(rawReq))
111+
req, err := http.NewRequest(http.MethodPost, baseURL+"/v1/chat/completions", bytes.NewReader(rawReq))
104112
if err != nil {
105113
return cm.Err[cm.Result[string, string, string]](
106114
fmt.Sprintf("failed to create request: %v", err),

activity/llm/openai-js/src/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import axios from 'axios';
22
import { getEnvironment } from 'wasi:cli/environment@0.2.3';
33

4+
const ENV_OPENAI_API_KEY = "OPENAI_API_KEY";
5+
const ENV_OPENAI_API_BASE_URL = "OPENAI_API_BASE_URL";
6+
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com";
7+
48
export const llm = {
59
async respond(userPrompt, settingsString) {
610
console.log("Responding to", userPrompt, settingsString);
7-
const ENV_OPENAI_API_KEY = "OPENAI_API_KEY";
811
// TODO: Switch to `process.env[ENV_OPENAI_API_KEY]` when https://github.com/bytecodealliance/ComponentizeJS/issues/190 is resolved.
9-
const apiKey = new Map(getEnvironment()).get(ENV_OPENAI_API_KEY);
12+
const envMap = new Map(getEnvironment());
13+
const apiKey = envMap.get(ENV_OPENAI_API_KEY);
14+
const baseUrl = envMap.get(ENV_OPENAI_API_BASE_URL) || DEFAULT_OPENAI_BASE_URL;
1015

1116
if (!apiKey) {
1217
throw `${ENV_OPENAI_API_KEY} must be set as an environment variable or passed in options`;
@@ -41,7 +46,7 @@ export const llm = {
4146
let response;
4247
try {
4348
response = await axios.post(
44-
"https://api.openai.com/v1/chat/completions",
49+
`${baseUrl}/v1/chat/completions`,
4550
requestBody,
4651
{
4752
headers: {

activity/llm/openai/src/lib.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use wstd::{
88
};
99

1010
const ENV_OPENAI_API_KEY: &str = "OPENAI_API_KEY";
11+
const ENV_OPENAI_API_BASE_URL: &str = "OPENAI_API_BASE_URL";
12+
const DEFAULT_OPENAI_BASE_URL: &str = "https://api.openai.com";
1113

1214
mod generated {
1315
#![allow(clippy::empty_line_after_outer_attr)]
@@ -65,6 +67,9 @@ async fn respond(user_prompt: String, settings: String) -> Result<String, String
6567
let api_key = env::var(ENV_OPENAI_API_KEY)
6668
.map_err(|_| format!("{ENV_OPENAI_API_KEY} must be set as an environment variable"))?;
6769

70+
let base_url = env::var(ENV_OPENAI_API_BASE_URL)
71+
.unwrap_or_else(|_| DEFAULT_OPENAI_BASE_URL.to_string());
72+
6873
let settings: Settings =
6974
serde_json::from_str(&settings).expect("`settings_json` must be parseable");
7075

@@ -84,7 +89,7 @@ async fn respond(user_prompt: String, settings: String) -> Result<String, String
8489
.header("Authorization", format!("Bearer {api_key}"))
8590
.header("Content-Type", "application/json")
8691
.method(Method::POST)
87-
.uri("https://api.openai.com/v1/chat/completions")
92+
.uri(format!("{base_url}/v1/chat/completions"))
8893
.body(
8994
Body::from_json(&request_body)
9095
.map_err(|err| format!("cannot serialize the request - {err:?}"))?,
@@ -120,7 +125,7 @@ impl Guest for Component {
120125
#[cfg(test)]
121126
mod tests {
122127
use crate::Component;
123-
use crate::ENV_OPENAI_API_KEY;
128+
use crate::{ENV_OPENAI_API_KEY, ENV_OPENAI_API_BASE_URL};
124129
use crate::generated::exports::stargazers::llm::llm::Guest;
125130
use crate::{Message, Role, Settings};
126131

@@ -129,6 +134,9 @@ mod tests {
129134
panic!("TEST_{ENV_OPENAI_API_KEY} must be set as an environment variable")
130135
});
131136
unsafe { std::env::set_var(ENV_OPENAI_API_KEY, test_token) };
137+
if let Ok(base_url) = std::env::var(format!("TEST_{ENV_OPENAI_API_BASE_URL}")) {
138+
unsafe { std::env::set_var(ENV_OPENAI_API_BASE_URL, base_url) };
139+
}
132140
}
133141

134142
#[test]

obelisk-local-go-activity.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ enabled = true
88
name = "activity_llm_openai_go"
99
location.path = "${OBELISK_TOML_DIR}/activity/llm/openai-go/dist/openai-go.wasm"
1010
exec.lock_expiry.seconds = 10
11-
env_vars = ["OPENAI_API_KEY"]
11+
env_vars = ["OPENAI_API_KEY", "OPENAI_API_BASE_URL"]
1212
forward_stdout = "stderr"
1313
forward_stderr = "stderr"
1414

obelisk-local-go-all.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ enabled = true
88
name = "activity_llm_openai_go"
99
location.path = "${OBELISK_TOML_DIR}/activity/llm/openai-go/dist/openai-go.wasm"
1010
exec.lock_expiry.seconds = 10
11-
env_vars = ["OPENAI_API_KEY"]
11+
env_vars = ["OPENAI_API_KEY", "OPENAI_API_BASE_URL"]
1212
forward_stdout = "stderr"
1313
forward_stderr = "stderr"
1414

obelisk-local-go-webhook.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ enabled = true
88
name = "activity_llm_openai"
99
location.path = "${OBELISK_TOML_DIR}/target/wasm32-wasip2/release/activity_llm_openai.wasm"
1010
exec.lock_expiry.seconds = 10
11-
env_vars = ["OPENAI_API_KEY"]
11+
env_vars = ["OPENAI_API_KEY", "OPENAI_API_BASE_URL"]
1212
forward_stdout = "stderr"
1313
forward_stderr = "stderr"
1414

obelisk-local-go-workflow.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ enabled = true
88
name = "activity_llm_openai"
99
location.path = "${OBELISK_TOML_DIR}/target/wasm32-wasip2/release/activity_llm_openai.wasm"
1010
exec.lock_expiry.seconds = 10
11-
env_vars = ["OPENAI_API_KEY"]
11+
env_vars = ["OPENAI_API_KEY", "OPENAI_API_BASE_URL"]
1212
forward_stdout = "stderr"
1313
forward_stderr = "stderr"
1414

obelisk-local-js-activity.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ enabled = true
88
name = "openai_js"
99
location.path = "${OBELISK_TOML_DIR}/activity/llm/openai-js/dist/openai-js.wasm"
1010
exec.lock_expiry.seconds = 10
11-
env_vars = ["OPENAI_API_KEY"]
11+
env_vars = ["OPENAI_API_KEY", "OPENAI_API_BASE_URL"]
1212
forward_stdout = "stderr"
1313
forward_stderr = "stderr"
1414

0 commit comments

Comments
 (0)