Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion config/config.sample.edn
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@
;; :monthly-budget 10.00
;; estimated cost per image in USD (default: 0.045 for Gemini 3.1 Flash 512px)
;; :cost-per-image 0.045
}}
}
;; optional: custom Cloudflare AI Gateway URL or base URL for Gemini API proxying
;; (e.g. "https://gateway.ai.cloudflare.com/v1/ACCOUNT_ID/GATEWAY_ID/google-ai-studio")
;; :gateway-url ""
}
:giphy {:key ""},
:weather {:weatherbitio {:key "" :default {:zip ""}}},
:command {:prefix "!"
Expand Down
33 changes: 18 additions & 15 deletions src/yetibot/core/commands/agent.clj
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
(defn model [] (or (config-str [:gemini :agent :model]) "gemini-3.1-pro-preview"))

(defn gemini-key [] (config-str [:gemini :key]))
(defn gateway-url [] (config-str [:gemini :gateway-url]))
(defn cli-bin [] (or (config-str [:gemini :cli]) "gemini"))

(defn- config-num [path default]
Expand Down Expand Up @@ -498,21 +499,23 @@
"--prompt" (build-agent-prompt request context mentions)])
(.directory (io/file workdir))
(.redirectError java.lang.ProcessBuilder$Redirect/DISCARD))]
(doto (.environment pb)
(.put "GEMINI_API_KEY" (gemini-key))
(.put "GEMINI_CLI_TRUST_WORKSPACE" "true")
(.put "GH_TOKEN" (or token ""))
(.put "YETIBOT_PORT" (str (or (System/getenv "PORT") "3003")))
;; inject git config via env (no global state): a credential helper that
;; authenticates HTTPS pushes with GH_TOKEN, plus insteadOf rewrites so any
;; SSH-style github remote is forced to HTTPS (where the token applies).
(.put "GIT_CONFIG_COUNT" "3")
(.put "GIT_CONFIG_KEY_0" "credential.https://github.com.helper")
(.put "GIT_CONFIG_VALUE_0" git-credential-helper)
(.put "GIT_CONFIG_KEY_1" "url.https://github.com/.insteadOf")
(.put "GIT_CONFIG_VALUE_1" "git@github.com:")
(.put "GIT_CONFIG_KEY_2" "url.https://github.com/.insteadOf")
(.put "GIT_CONFIG_VALUE_2" "ssh://git@github.com/"))
(let [env (.environment pb)
env-map (cond-> {"GEMINI_API_KEY" (gemini-key)
"GEMINI_CLI_TRUST_WORKSPACE" "true"
"GH_TOKEN" (or token "")
"YETIBOT_PORT" (str (or (System/getenv "PORT") "3003"))
;; inject git config via env (no global state): a credential helper that
;; authenticates HTTPS pushes with GH_TOKEN, plus insteadOf rewrites so any
;; SSH-style github remote is forced to HTTPS (where the token applies).
"GIT_CONFIG_COUNT" "3"
"GIT_CONFIG_KEY_0" "credential.https://github.com.helper"
"GIT_CONFIG_VALUE_0" git-credential-helper
"GIT_CONFIG_KEY_1" "url.https://github.com/.insteadOf"
"GIT_CONFIG_VALUE_1" "git@github.com:"
"GIT_CONFIG_KEY_2" "url.https://github.com/.insteadOf"
"GIT_CONFIG_VALUE_2" "ssh://git@github.com/"}
(gateway-url) (assoc "GOOGLE_GEMINI_BASE_URL" (gateway-url)))]
(.putAll env env-map))
(info "running gemini agent" (cli-bin) "in" (str workdir))
(let [proc (.start pb)
timed-out (atom false)
Expand Down
19 changes: 12 additions & 7 deletions src/yetibot/core/util/gemini.clj
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,20 @@
(s/def ::per (s/or :string string? :number number?))
(s/def ::monthly (s/keys :opt-un [::budget]))
(s/def ::budget (s/or :string string? :number number?))
(s/def ::gateway-url string?)

(s/def ::config (s/keys :req-un [::key] :opt-un [::cost ::monthly]))
(s/def ::config (s/keys :req-un [::key] :opt-un [::cost ::monthly ::gateway-url]))

;; All Gemini settings live under [:gemini] and share a single API key
;; (YB_GEMINI_KEY) with the rest of Yetibot's Gemini features.
(def config (or (:value (get-config ::config [:gemini])) {}))

(defn- api-base-url []
(or (:gateway-url config) "https://generativelanguage.googleapis.com"))

(defn- api-base-v1beta []
(str (api-base-url) "/v1beta"))

(def default-model "gemini-3.1-flash-image-preview")

(defn gemini-model [] default-model)
Expand Down Expand Up @@ -232,8 +239,8 @@
(let [api-key (:key config)
model (gemini-model)
url (format
"https://generativelanguage.googleapis.com/v1beta/models/%s:generateContent?key=%s"
model api-key)
"%s/models/%s:generateContent?key=%s"
(api-base-v1beta) model api-key)
image-parts (when (seq image-urls)
(keep url->inline-part image-urls))
parts (into [{:text prompt}] image-parts)
Expand Down Expand Up @@ -273,8 +280,6 @@
;; Veo is asynchronous: start a long-running operation, poll until done, then
;; download the resulting mp4. Settings live under [:gemini :veo].

(def ^:private api-base "https://generativelanguage.googleapis.com/v1beta")

(def default-veo-model "veo-3.1-lite-generate-preview")

(defn veo-model [] (or (-> config :veo :model) default-veo-model))
Expand Down Expand Up @@ -331,7 +336,7 @@
[prompt image model duration]
(let [model (or model (veo-model))
duration (or duration (veo-duration))
url (format "%s/models/%s:predictLongRunning?key=%s" api-base model (:key config))
url (format "%s/models/%s:predictLongRunning?key=%s" (api-base-v1beta) model (:key config))
body {:instances [(cond-> {:prompt prompt} image (assoc :image image))]
:parameters {:aspectRatio "16:9" :durationSeconds duration}}
resp (client/post url (merge veo-http-timeouts
Expand All @@ -350,7 +355,7 @@
"Poll a Veo operation until done; returns the completed operation body."
[op-name]
(loop [n 0]
(let [body (:body (client/get (format "%s/%s?key=%s" api-base op-name (:key config))
(let [body (:body (client/get (format "%s/%s?key=%s" (api-base-v1beta) op-name (:key config))
(merge veo-http-timeouts
{:as :json :throw-exceptions false})))]
(cond
Expand Down
8 changes: 7 additions & 1 deletion test/yetibot/core/test/commands/agent.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[clojure.string :as string]
[clojure.data.json :as json]
[clj-http.client :as client]
[yetibot.core.config :refer [get-config]]
[yetibot.core.commands.agent :as agent])
(:import
[java.security KeyPairGenerator Signature]
Expand Down Expand Up @@ -80,7 +81,12 @@
(fact "default max turns is 50"
(agent/agent-max-turns) => 50)
(fact "default model is the current Gemini 3.1 Pro"
(agent/model) => "gemini-3.1-pro-preview"))
(agent/model) => "gemini-3.1-pro-preview")
(fact "default gateway url is nil"
(agent/gateway-url) => nil)
(fact "gateway url can be overridden by config"
(agent/gateway-url) => "https://my-gateway.com"
(provided (get-config anything [:gemini :gateway-url]) => {:value "https://my-gateway.com"})))

(facts "about mention-glossary"
(fact "prefers the server nickname and keeps the <@id> token"
Expand Down