99from utils .llm_config import get_llm_config
1010from utils .llm_provider import get_llm_provider , get_model
1111from utils .llm_utils import extract_text
12+ from utils .template_vision_errors import (
13+ VISION_LAYOUT_USER_MESSAGE ,
14+ is_likely_vision_capability_error ,
15+ )
1216
1317MAX_ATTEMPTS_PER_PROVIDER = 4
14- SUPPORTED_TEMPLATE_PROVIDERS = (
15- LLMProvider .OPENAI ,
16- LLMProvider .CODEX ,
17- LLMProvider .GOOGLE ,
18- LLMProvider .ANTHROPIC ,
19- LLMProvider .AZURE ,
20- )
2118
2219
2320def _exception_message (exc : Exception ) -> str :
@@ -32,17 +29,9 @@ def _exception_message(exc: Exception) -> str:
3229 return " " .join (message .split ())[:500 ]
3330
3431
35- def _unsupported_template_provider_message () -> str :
36- return (
37- "Template generation only supports OpenAI, Codex, Google, Anthropic, or Azure OpenAI."
38- )
39-
40-
41- def _supported_template_provider_or_raise () -> tuple [LLMProvider , str ]:
42- provider = get_llm_provider ()
43- if provider not in SUPPORTED_TEMPLATE_PROVIDERS :
44- raise HTTPException (status_code = 400 , detail = _unsupported_template_provider_message ())
45- return provider , get_model ()
32+ def _resolve_template_provider_and_model () -> tuple [LLMProvider , str ]:
33+ """Uses the configured text LLM; slide layout generation requires vision (image parts)."""
34+ return get_llm_provider (), get_model ()
4635
4736
4837def _provider_label (provider : LLMProvider ) -> str :
@@ -52,10 +41,22 @@ def _provider_label(provider: LLMProvider) -> str:
5241 return "Codex"
5342 if provider == LLMProvider .GOOGLE :
5443 return "Google"
44+ if provider == LLMProvider .VERTEX :
45+ return "Vertex AI"
5546 if provider == LLMProvider .ANTHROPIC :
5647 return "Anthropic"
5748 if provider == LLMProvider .AZURE :
5849 return "Azure OpenAI"
50+ if provider == LLMProvider .OLLAMA :
51+ return "Ollama"
52+ if provider == LLMProvider .OPENROUTER :
53+ return "OpenRouter"
54+ if provider == LLMProvider .CEREBRAS :
55+ return "Cerebras"
56+ if provider == LLMProvider .CUSTOM :
57+ return "Custom"
58+ if provider == LLMProvider .LITELLM :
59+ return "LiteLLM"
5960 return "Template provider"
6061
6162
@@ -108,6 +109,7 @@ async def _run_template_llm_with_retries(
108109 * ,
109110 provider_label : str ,
110111 call : Callable [[], Awaitable [str ]],
112+ requires_vision : bool = False ,
111113) -> str :
112114 last_exception : Optional [Exception ] = None
113115
@@ -118,10 +120,18 @@ async def _run_template_llm_with_retries(
118120 return response_text
119121 raise ValueError ("No output from template generation provider" )
120122 except HTTPException as exc :
123+ if requires_vision and is_likely_vision_capability_error (exc ):
124+ raise HTTPException (
125+ status_code = 400 , detail = VISION_LAYOUT_USER_MESSAGE
126+ ) from exc
121127 if 400 <= exc .status_code < 500 :
122128 raise exc
123129 last_exception = exc
124130 except Exception as exc :
131+ if requires_vision and is_likely_vision_capability_error (exc ):
132+ raise HTTPException (
133+ status_code = 400 , detail = VISION_LAYOUT_USER_MESSAGE
134+ ) from exc
125135 last_exception = exc
126136
127137 if isinstance (last_exception , HTTPException ):
@@ -141,7 +151,7 @@ def _template_provider_label_and_call(
141151 image_bytes : Optional [bytes ] = None ,
142152 media_type : str = "image/png" ,
143153) -> tuple [str , Callable [[], Awaitable [str ]]]:
144- provider , model = _supported_template_provider_or_raise ()
154+ provider , model = _resolve_template_provider_and_model ()
145155 label = _provider_label (provider )
146156 return (
147157 label ,
@@ -168,7 +178,9 @@ async def generate_slide_layout_code(
168178 image_bytes = image_bytes ,
169179 media_type = media_type ,
170180 )
171- return await _run_template_llm_with_retries (provider_label = label , call = call )
181+ return await _run_template_llm_with_retries (
182+ provider_label = label , call = call , requires_vision = True
183+ )
172184
173185
174186async def edit_slide_layout_code (
0 commit comments