Skip to content

Instantly share code, notes, and snippets.

@ikitommi
Last active September 23, 2024 17:33
Show Gist options
  • Save ikitommi/e643713719c3620f943ef34086451c69 to your computer and use it in GitHub Desktop.
Save ikitommi/e643713719c3620f943ef34086451c69 to your computer and use it in GitHub Desktop.
OpenAI API Structured Outputs in Clojure with Malli
(ns summer.demo
(:require [summer.openai :as openai]))
(def Step
[:map
[:explanation :string]
[:output :string]])
(def MathResponse
[:map {:name "mathresponse"}
[:steps [:vector Step]]
[:final_answer :string]])
(openai/completion
{:model "gpt-4o-2024-08-06",
:messages [{:role "system",
:content "You are a helpful math tutor. Only use the schema for math responses."},
{:role "user"
:content "how can I solve 8x + 7 = -23"}]
:response_format (openai/json-schema-response MathResponse)})
(ns summer.openai
(:require [hato.client :as c]
[jsonista.core :as j]
[malli.core :as m]
[malli.json-schema :as json-schema]
[malli.util :as mu]))
(defn <-json [x]
(j/read-value x j/keyword-keys-object-mapper))
(defn <-response [response]
(update response :choices (partial mapv #(update-in % [:message :content] <-json))))
(defn json-schema-response [?schema]
(let [schema (mu/closed-schema ?schema)]
{:type "json_schema"
:json_schema {:name (some-> schema (m/properties) :name (or "response"))
:schema (json-schema/transform schema)
:strict true}}))
(defn completion [data]
(-> (c/post "https://api.openai.com/v1/chat/completions"
{:headers {"Content-Type" "application/json"
"Authorization" (str "Bearer " (System/getenv "OPENAI_API_KEY"))}
:body (j/write-value-as-string data)})
:body
(<-json)
(<-response)))
{:steps [{:explanation "Start by isolating the term with x. Subtract 7 from both sides of the equation to do this.",
:output "8x + 7 - 7 = -23 - 7"}
{:explanation "Simplify both sides of the equation. On the left side, 7 - 7 cancels out, and on the right side, -23 - 7 equals -30.",
:output "8x = -30"}
{:explanation "To solve for x, divide both sides of the equation by 8.",
:output "8x/8 = -30/8"}
{:explanation "Simplify the right side by performing the division.",
:output "x = -3.75"}],
:final_answer "x = -3.75"}
@tkataja
Copy link

tkataja commented Aug 9, 2024

Nice. Got some inspiration to implement one mindmapping graph idea I have wanted to try: https://github.com/tkataja/bb-malli-openai-mindmap

Using recursive Malli schemas etc. was really powerful here.

@ikitommi
Copy link
Author

@tkataja looks great 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment