Skip to content

Commit efaba5c

Browse files
northdpoleBornunique911
authored andcommitted
fix: handle AI quota limits and configurable models
Resolves the "Resource Exhausted" error when using a standard API key by defaulting to the correct embedding model (`embedding-001`). Also introduces environment variables for configuring the chat and embedding models, and improves exception handling to correctly catch and report rate limiting and quota issues to the user.
1 parent ec0593c commit efaba5c

4 files changed

Lines changed: 47 additions & 22 deletions

File tree

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ LOGIN_ALLOWED_DOMAINS=example.com
3838
# GCP
3939

4040
GCP_NATIVE=false
41+
GEMINI_API_KEY=your-gemini-api-key
42+
VERTEX_CHAT_MODEL=gemini-2.0-flash
43+
VERTEX_EMBED_CONTENT_MODEL=embedding-001
4144

4245
# Spreadsheet Auth
4346

application/prompt_client/vertex_prompt_client.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class VertexPromptClient:
131131

132132
def __init__(self) -> None:
133133
self.client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))
134-
self.model_name = "gemini-2.0-flash"
134+
self.model_name = os.environ.get("VERTEX_CHAT_MODEL", "gemini-2.0-flash")
135135

136136
def get_model_name(self) -> str:
137137
"""Return the model name being used."""
@@ -153,7 +153,11 @@ def _with_genai_rate_limit_retry(
153153
for attempt in range(max_retries + 1):
154154
try:
155155
return fn()
156-
except genai.errors.ClientError as e:
156+
except (
157+
genai.errors.ClientError,
158+
googleExceptions.GoogleAPICallError,
159+
grpc.RpcError,
160+
) as e:
157161
_log_genai_client_error(context, e)
158162
if not _is_genai_rate_limit_error(e) or attempt >= max_retries:
159163
raise
@@ -205,9 +209,7 @@ def _truncate_one(t: str) -> str:
205209
result = self.client.models.embed_content(
206210
# Use a stable embeddings model that is supported for `embedContent`
207211
# across API versions.
208-
model=os.environ.get(
209-
"VERTEX_EMBED_CONTENT_MODEL", "gemini-embedding-001"
210-
),
212+
model=os.environ.get("VERTEX_EMBED_CONTENT_MODEL", "embedding-001"),
211213
contents=texts if is_batch else texts[0],
212214
config=types.EmbedContentConfig(task_type="SEMANTIC_SIMILARITY"),
213215
)
@@ -217,7 +219,11 @@ def _truncate_one(t: str) -> str:
217219
if is_batch:
218220
return [emb.values for emb in result.embeddings]
219221
return result.embeddings[0].values
220-
except genai.errors.ClientError as e:
222+
except (
223+
genai.errors.ClientError,
224+
googleExceptions.GoogleAPICallError,
225+
grpc.RpcError,
226+
) as e:
221227
_log_genai_client_error("embed_content", e)
222228
if not _is_genai_rate_limit_error(e) or attempt >= max_retries:
223229
raise
@@ -262,7 +268,7 @@ def create_chat_completion(self, prompt, closest_object_str) -> str:
262268

263269
def _call() -> Any:
264270
response = self.client.models.generate_content(
265-
model="gemini-2.0-flash",
271+
model=self.model_name,
266272
contents=msg,
267273
config=types.GenerateContentConfig(
268274
max_output_tokens=MAX_OUTPUT_TOKENS, temperature=0.5
@@ -279,7 +285,7 @@ def query_llm(self, raw_question: str) -> str:
279285

280286
def _call() -> Any:
281287
response = self.client.models.generate_content(
282-
model="gemini-2.0-flash",
288+
model=self.model_name,
283289
contents=msg,
284290
config=types.GenerateContentConfig(
285291
max_output_tokens=MAX_OUTPUT_TOKENS, temperature=0.5

application/tests/chat_completion_test.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def test_completion_returns_503_json_on_gemini_429(self) -> None:
5050
self.assertIn("error", data)
5151
self.assertIn("rate-limited", data["error"])
5252

53-
def test_completion_propagates_non_429_genai_error(self) -> None:
53+
def test_completion_returns_500_on_non_429_genai_error(self) -> None:
5454
os.environ["NO_LOGIN"] = "1"
5555
err = genai_errors.ClientError(
5656
400,
@@ -66,13 +66,15 @@ def test_completion_propagates_non_429_genai_error(self) -> None:
6666
with patch("application.prompt_client.prompt_client.PromptHandler") as mock_ph:
6767
mock_ph.return_value.generate_text.side_effect = err
6868
with self.app.test_client() as client:
69-
with self.assertRaises(genai_errors.ClientError) as ctx:
70-
client.post(
71-
"/rest/v1/completion",
72-
json={"prompt": "test"},
73-
content_type="application/json",
74-
)
75-
self.assertIs(ctx.exception, err)
69+
response = client.post(
70+
"/rest/v1/completion",
71+
json={"prompt": "test"},
72+
content_type="application/json",
73+
)
74+
self.assertEqual(500, response.status_code)
75+
data = json.loads(response.data)
76+
self.assertIn("error", data)
77+
self.assertIn("AI Service Error", data["error"])
7678

7779

7880
if __name__ == "__main__":

application/web/web_main.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -919,26 +919,40 @@ def chat_cre() -> Any:
919919
database = db.Node_collection()
920920
# Lazy import to avoid loading heavy prompt/ML dependencies at web boot.
921921
from google.genai import errors as genai_errors
922-
from application.prompt_client import prompt_client
922+
from google.api_core import exceptions as googleExceptions
923+
import grpc
924+
from application.prompt_client import prompt_client, vertex_prompt_client
923925

924926
prompt = prompt_client.PromptHandler(database)
925927
try:
926928
response = prompt.generate_text(message.get("prompt"))
927-
except genai_errors.ClientError as e:
928-
# google.genai APIError uses ``code`` (HTTP status), not ``status_code``.
929-
if getattr(e, "code", None) == 429:
929+
except (
930+
genai_errors.ClientError,
931+
googleExceptions.GoogleAPICallError,
932+
grpc.RpcError,
933+
) as e:
934+
if vertex_prompt_client._is_genai_rate_limit_error(e):
930935
return (
931936
jsonify(
932937
{
933938
"error": (
934-
"The AI service is temporarily rate-limited. "
939+
"The AI service is temporarily rate-limited or out of quota. "
935940
"Please try again in a minute."
936941
)
937942
}
938943
),
939944
503,
940945
)
941-
raise
946+
return (
947+
jsonify({"error": f"AI Service Error: {str(e)}"}),
948+
500,
949+
)
950+
except Exception as e:
951+
logger.exception("Unexpected error during chatbot completion")
952+
return (
953+
jsonify({"error": f"An unexpected error occurred: {str(e)}"}),
954+
500,
955+
)
942956
return jsonify(response)
943957

944958

0 commit comments

Comments
 (0)