2525
2626def _get_openai_models (api_key : Optional [str ] = None ) -> List [str ]:
2727 """Fetch available chat/completion models from OpenAI API.
28-
28+
2929 Dynamically discovers all chat/completion models suitable for economic analysis.
3030 Excludes embeddings, fine-tuning, and other non-chat models.
3131 """
3232 if openai is None :
3333 return []
34-
34+
3535 if api_key is None :
3636 api_key = os .getenv ("OPENAI_API_KEY" )
37-
37+
3838 if not api_key :
3939 return []
40-
40+
4141 try :
4242 client = openai .OpenAI (api_key = api_key )
4343 models = client .models .list ()
44-
44+
4545 # Filter for chat/completion models by excluding known non-chat types
4646 chat_models = []
4747 for model in models .data :
4848 model_id = model .id .lower ()
49-
49+
5050 # Exclude embeddings, fine-tuned models, and other non-chat models
5151 exclude_patterns = [
5252 "text-embedding" ,
@@ -58,91 +58,96 @@ def _get_openai_models(api_key: Optional[str] = None) -> List[str]:
5858 "curie-" , # Old curie models
5959 "davinci-" , # Old davinci models
6060 ]
61-
61+
6262 # Check if model should be excluded
6363 should_exclude = any (pattern in model_id for pattern in exclude_patterns )
64-
64+
6565 # Include all models that aren't excluded (covers gpt-4, gpt-3.5, o1, gpt-5, and any future chat models)
6666 if not should_exclude :
6767 # Get the original case model ID
6868 chat_models .append (model .id )
69-
69+
7070 return sorted (chat_models )
7171 except Exception as e :
7272 raise ValueError (f"Error fetching OpenAI models: { e } " )
7373
7474
7575def _get_anthropic_models (api_key : Optional [str ] = None ) -> List [str ]:
7676 """Fetch available chat/completion models from Anthropic API.
77-
77+
7878 Returns all currently available Claude models (all are chat/completion models).
7979 Note: Anthropic may not provide a models list endpoint - if so, this will return an empty list.
8080 """
8181 if anthropic is None :
8282 return []
83-
83+
8484 if api_key is None :
8585 api_key = os .getenv ("ANTHROPIC_API_KEY" )
86-
86+
8787 if not api_key :
8888 return []
89-
89+
9090 try :
9191 client = anthropic .Anthropic (api_key = api_key )
92-
92+
9393 # Try to use the models list endpoint if available
94- if hasattr (client , ' models' ) and hasattr (client .models , ' list' ):
94+ if hasattr (client , " models" ) and hasattr (client .models , " list" ):
9595 try :
9696 models_response = client .models .list ()
9797 # Extract model IDs from the response
98- if hasattr (models_response , ' data' ):
98+ if hasattr (models_response , " data" ):
9999 return sorted ([model .id for model in models_response .data ])
100100 elif isinstance (models_response , list ):
101- return sorted ([model .id if hasattr (model , 'id' ) else str (model ) for model in models_response ])
101+ return sorted (
102+ [
103+ model .id if hasattr (model , "id" ) else str (model )
104+ for model in models_response
105+ ]
106+ )
102107 except AttributeError :
103108 # Fall through - models.list() may not be implemented
104109 pass
105110 except Exception as e :
106111 # If models.list() exists but fails, raise the error
107112 raise ValueError (f"Error calling Anthropic models.list(): { e } " )
108-
113+
109114 # Anthropic doesn't provide a public models list endpoint
110115 # Return empty list since we can't dynamically discover models
111116 return []
112-
117+
113118 except Exception as e :
114119 raise ValueError (f"Error fetching Anthropic models: { e } " )
115120
116121
117122def _get_gemini_models (api_key : Optional [str ] = None ) -> List [str ]:
118123 """Fetch available chat/completion models from Google Gemini API.
119-
124+
120125 Filters for models that support chat/completion (generateContent).
121126 Excludes embeddings and other non-chat models.
122127 """
123128 if genai is None :
124129 return []
125-
130+
126131 if api_key is None :
127132 api_key = os .getenv ("GEMINI_API_KEY" )
128-
133+
129134 if not api_key :
130135 return []
131-
136+
132137 try :
133138 genai .configure (api_key = api_key )
134-
139+
135140 # List available models
136141 models = genai .list_models ()
137-
142+
138143 # Filter for chat/completion models (those that support generateContent)
139144 chat_models = []
140145 for model in models :
141146 # Only include models that support generateContent (chat/completion)
142147 if "generateContent" in model .supported_generation_methods :
143148 model_name = model .name .replace ("models/" , "" )
144149 chat_models .append (model_name )
145-
150+
146151 return sorted (chat_models )
147152 except Exception as e :
148153 raise ValueError (f"Error fetching Gemini models: { e } " )
@@ -159,7 +164,7 @@ def fetch_available_ai_models(
159164) -> dg .MaterializeResult :
160165 """
161166 Fetch available chat/completion models from OpenAI, Anthropic, and Gemini APIs and store results.
162-
167+
163168 This asset queries each provider's API to get the current list of available chat/completion models
164169 suitable for economic analysis. Filters out embeddings, fine-tuning models, and other non-chat models.
165170 Stores them in the database for reference and configuration.
@@ -168,15 +173,15 @@ def fetch_available_ai_models(
168173 env = os .getenv ("ENVIRONMENT" , "not set" )
169174 context .log .info (f"Environment: { env } " )
170175 context .log .info ("Starting to fetch available AI models from all providers..." )
171-
176+
172177 results : Dict [str , Any ] = {
173178 "openai" : [],
174179 "anthropic" : [],
175180 "gemini" : [],
176181 }
177-
182+
178183 errors : Dict [str , str ] = {}
179-
184+
180185 # Fetch OpenAI models
181186 context .log .info ("Fetching OpenAI models..." )
182187 try :
@@ -187,7 +192,7 @@ def fetch_available_ai_models(
187192 error_msg = str (e )
188193 errors ["openai" ] = error_msg
189194 context .log .warning (f"Failed to fetch OpenAI models: { error_msg } " )
190-
195+
191196 # Fetch Anthropic models
192197 context .log .info ("Fetching Anthropic models..." )
193198 try :
@@ -204,7 +209,7 @@ def fetch_available_ai_models(
204209 error_msg = str (e )
205210 errors ["anthropic" ] = error_msg
206211 context .log .warning (f"Failed to fetch Anthropic models: { error_msg } " )
207-
212+
208213 # Fetch Gemini models
209214 context .log .info ("Fetching Gemini models..." )
210215 try :
@@ -215,7 +220,7 @@ def fetch_available_ai_models(
215220 error_msg = str (e )
216221 errors ["gemini" ] = error_msg
217222 context .log .warning (f"Failed to fetch Gemini models: { error_msg } " )
218-
223+
219224 # Prepare result for database
220225 fetch_timestamp = datetime .now ()
221226 result = {
@@ -232,13 +237,15 @@ def fetch_available_ai_models(
232237 "dagster_run_id" : context .run_id ,
233238 "dagster_asset_key" : str (context .asset_key ),
234239 }
235-
240+
236241 # Write to database (drop and recreate table)
237- context .log .info ("Writing model list to database (dropping and recreating table)..." )
242+ context .log .info (
243+ "Writing model list to database (dropping and recreating table)..."
244+ )
238245 try :
239246 # Convert result dict to Polars DataFrame
240247 df = pl .DataFrame ([result ])
241-
248+
242249 # Drop and recreate table with new data
243250 md .drop_create_duck_db_table (
244251 table_name = "available_ai_models" ,
@@ -251,37 +258,42 @@ def fetch_available_ai_models(
251258 # Add database error to errors dict
252259 errors ["database" ] = error_msg
253260 # Still return results in metadata even if database write fails
254- context .log .warning ("Continuing despite database write failure - results available in metadata" )
255-
261+ context .log .warning (
262+ "Continuing despite database write failure - results available in metadata"
263+ )
264+
256265 # Prepare metadata
257266 metadata = {
258267 "openai_models_count" : len (results ["openai" ]),
259268 "anthropic_models_count" : len (results ["anthropic" ]),
260269 "gemini_models_count" : len (results ["gemini" ]),
261270 "fetch_timestamp" : fetch_timestamp .isoformat (),
262271 }
263-
272+
264273 # Add model lists to metadata (truncated if too long)
265274 if results ["openai" ]:
266275 metadata ["openai_models" ] = results ["openai" ][:10 ] # First 10 models
267276 if len (results ["openai" ]) > 10 :
268- metadata ["openai_models_note" ] = f"Showing first 10 of { len (results ['openai' ])} models"
269-
277+ metadata ["openai_models_note" ] = (
278+ f"Showing first 10 of { len (results ['openai' ])} models"
279+ )
280+
270281 if results ["anthropic" ]:
271282 metadata ["anthropic_models" ] = results ["anthropic" ]
272-
283+
273284 if results ["gemini" ]:
274285 metadata ["gemini_models" ] = results ["gemini" ][:10 ] # First 10 models
275286 if len (results ["gemini" ]) > 10 :
276- metadata ["gemini_models_note" ] = f"Showing first 10 of { len (results ['gemini' ])} models"
277-
287+ metadata ["gemini_models_note" ] = (
288+ f"Showing first 10 of { len (results ['gemini' ])} models"
289+ )
290+
278291 if errors :
279292 metadata ["errors" ] = errors
280-
293+
281294 context .log .info (
282295 f"Successfully fetched models: OpenAI={ len (results ['openai' ])} , "
283296 f"Anthropic={ len (results ['anthropic' ])} , Gemini={ len (results ['gemini' ])} "
284297 )
285-
286- return dg .MaterializeResult (metadata = metadata )
287298
299+ return dg .MaterializeResult (metadata = metadata )
0 commit comments