Create an OpenAI inference component based on the ETH Price Oracle (components/eth-price-oracle). The component should:
- Accept input in the format 'PROMPT|OPENAI_API_KEY|SEED' from a Makefile parameter
- Use
fetch_json
andhttp_request_post_json
for API communication - Return and log only
choices[0].message.content
andchoices[0].finish_reason
- Include proper input validation and SEED should be an integer
- Use the main makefile, do not create a separate one.
Important Implementation Details:
- Create proper serializable structs for the request payload
- CRITICAL: Pass the request struct DIRECTLY to http_request_post_json, NOT a serialized string or byte array
- Use
#[derive(Debug, Serialize)]
for request structures and#[derive(Debug, Deserialize)]
for response structures - For OpenAIResponse, do NOT use #[serde(flatten)] on the error field
- Use serde_json::Value for the message field in the Choice struct to flexibly handle the message format
- For logging, use
crate::bindings::host::log
andcrate::bindings::host::LogLevel
instead of direct host imports - For setting headers, look at how the eth-price-oracle component does this with
http_request_get().headers_mut().insert()
pattern - Implement safe response parsing using
.get()
and.and_then()
methods - Check for API errors in the response before attempting to extract content
- Implement Display trait for OpenAIResponseData struct to enable proper logging
- Be sure to include the "Accept" header in addition to "Content-Type" and "Authorization"
The API request structure is:
{
"seed": $SEED,
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "<PROMPT>"}
]
}
The API response will be deserialized into a struct like this:
#[derive(Debug, Deserialize)]
struct OpenAIResponse {
id: Option<String>,
model: Option<String>,
choices: Vec<OpenAIChoice>,
error: Option<OpenAIError>, // NOT flattened
}
#[derive(Debug, Deserialize)]
struct OpenAIChoice {
message: serde_json::Value, // Use Value for flexibility
finish_reason: Option<String>,
}
Authorization header should be added to the request using:
req.headers_mut().insert(
"Authorization",
HeaderValue::from_str(&format!("Bearer {}", api_key)).map_err(|e| e.to_string())?,
);
The Makefile command should be:
ai-exec:
@$(WAVS_CMD) exec --log-level=info --data /data/.docker --home /data \
--component "/data/compiled/${AI_COMPONENT_FILENAME}" \
--input "$(PROMPT)|$(OPENAI_API_KEY)|$(SEED)"
Implement logging that shows request details (but not the API key) and response structure for debugging.