vectara-agentic 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vectara-agentic might be problematic. Click here for more details.

@@ -0,0 +1,415 @@
1
+ """
2
+ This module contains the ToolsFactory class for creating agent tools.
3
+ """
4
+
5
+ import inspect
6
+ import re
7
+ import importlib
8
+
9
+ from typing import Callable, List, Any, Optional
10
+ from pydantic import BaseModel, Field
11
+
12
+ from llama_index.core.tools import FunctionTool
13
+ from llama_index.core.base.response.schema import Response
14
+ from llama_index.indices.managed.vectara import VectaraIndex
15
+ from llama_index.core.utilities.sql_wrapper import SQLDatabase
16
+
17
+ from .types import ToolType
18
+ from .tools_catalog import (
19
+ # General tools
20
+ summarize_text,
21
+ rephrase_text,
22
+ critique_text,
23
+ # Guardrail tools
24
+ guardrails_no_politics,
25
+ guardrails_be_polite,
26
+ )
27
+
28
+ LI_packages = {
29
+ "yahoo_finance": ToolType.QUERY,
30
+ "arxiv": ToolType.QUERY,
31
+ "tavily_research": ToolType.QUERY,
32
+ "database": ToolType.QUERY,
33
+ "google": {
34
+ "GmailToolSpec": {
35
+ "load_data": ToolType.QUERY,
36
+ "search_messages": ToolType.QUERY,
37
+ "create_draft": ToolType.ACTION,
38
+ "update_draft": ToolType.ACTION,
39
+ "get_draft": ToolType.QUERY,
40
+ "send_draft": ToolType.ACTION,
41
+ },
42
+ "GoogleCalendarToolSpec": {
43
+ "load_data": ToolType.QUERY,
44
+ "create_event": ToolType.ACTION,
45
+ "get_date": ToolType.QUERY,
46
+ },
47
+ "GoogleSearchToolSpec": {"google_search": ToolType.QUERY},
48
+ },
49
+ }
50
+
51
+
52
+ class VectaraTool:
53
+ """
54
+ A wrapper of FunctionTOol class for Vectara tools, adding the tool_type attribute.
55
+ """
56
+
57
+ def __init__(self, function_tool: FunctionTool, tool_type: ToolType) -> None:
58
+ self.function_tool = function_tool
59
+ self.tool_type = tool_type
60
+
61
+ def __getattr__(self, name):
62
+ return getattr(self.function_tool, name)
63
+
64
+ def __call__(self, *args, **kwargs):
65
+ return self.function_tool(*args, **kwargs)
66
+
67
+
68
+ class VectaraToolFactory:
69
+ """
70
+ A factory class for creating Vectara RAG tools.
71
+ """
72
+
73
+ def __init__(
74
+ self,
75
+ vectara_customer_id: str,
76
+ vectara_corpus_id: str,
77
+ vectara_api_key: str,
78
+ ) -> None:
79
+ self.vectara_customer_id = vectara_customer_id
80
+ self.vectara_corpus_id = vectara_corpus_id
81
+ self.vectara_api_key = vectara_api_key
82
+
83
+ def create_rag_tool(
84
+ self,
85
+ tool_name: str,
86
+ tool_description: str,
87
+ tool_args_schema: type[BaseModel],
88
+ vectara_summarizer: str = "vectara-summary-ext-24-05-sml",
89
+ summary_num_results: int = 5,
90
+ summary_response_lang: str = "eng",
91
+ n_sentences_before: int = 2,
92
+ n_sentences_after: int = 2,
93
+ lambda_val: float = 0.005,
94
+ reranker: str = "mmr",
95
+ rerank_k: int = 50,
96
+ mmr_diversity_bias: float = 0.2,
97
+ include_citations: bool = True,
98
+ ) -> VectaraTool:
99
+ """
100
+ Creates a RAG (Retrieve and Generate) tool.
101
+
102
+ Parameters:
103
+ - tool_name (str): The name of the tool.
104
+ - tool_description (str): The description of the tool.
105
+ - tool_args_schema (BaseModel): The schema for the tool arguments.
106
+ - vectara_summarizer (str): The Vectara summarizer to use.
107
+ - summary_num_results (int): The number of summary results.
108
+ - summary_response_lang (str): The response language for the summary.
109
+ - n_sentences_before (int): Number of sentences before the summary.
110
+ - n_sentences_after (int): Number of sentences after the summary.
111
+ - lambda_val (float): Lambda value for the Vectara query.
112
+ - reranker (str): The reranker mode.
113
+ - rerank_k (int): Number of top-k documents for reranking.
114
+ - mmr_diversity_bias (float): MMR diversity bias.
115
+ - include_citations (bool): Whether to include citations in the response.
116
+ If True, uses MARKDOWN vectara citations that requires the Vectara scale plan.
117
+ """
118
+ vectara = VectaraIndex(
119
+ vectara_api_key=self.vectara_api_key,
120
+ vectara_customer_id=self.vectara_customer_id,
121
+ vectara_corpus_id=self.vectara_corpus_id,
122
+ )
123
+
124
+ def build_filter_string(kwargs):
125
+ filter_parts = []
126
+ for key, value in kwargs.items():
127
+ if value:
128
+ if isinstance(value, str):
129
+ filter_parts.append(f"doc.{key}='{value}'")
130
+ else:
131
+ filter_parts.append(f"doc.{key}={value}")
132
+ return " AND ".join(filter_parts)
133
+
134
+ # Dynamically generate the RAG function
135
+ def rag_function(*args, **kwargs) -> dict[str, Any]:
136
+ """Dynamically generated function for RAG query with Vectara"""
137
+ # Convert args to kwargs using the function signature
138
+ sig = inspect.signature(rag_function)
139
+ bound_args = sig.bind_partial(*args, **kwargs)
140
+ bound_args.apply_defaults()
141
+ kwargs = bound_args.arguments
142
+
143
+ query = kwargs.pop("query")
144
+ filter_string = build_filter_string(kwargs)
145
+
146
+ vectara_query_engine = vectara.as_query_engine(
147
+ summary_enabled=True,
148
+ summary_num_results=summary_num_results,
149
+ summary_response_lang=summary_response_lang,
150
+ summary_prompt_name=vectara_summarizer,
151
+ vectara_query_mode=reranker,
152
+ rerank_k=rerank_k,
153
+ mmr_diversity_bias=mmr_diversity_bias,
154
+ n_sentence_before=n_sentences_before,
155
+ n_sentence_after=n_sentences_after,
156
+ lambda_val=lambda_val,
157
+ filter=filter_string,
158
+ citations_url_pattern="{doc.url}" if include_citations else None,
159
+ )
160
+ response = vectara_query_engine.query(query)
161
+
162
+ if str(response) == "None":
163
+ return Response(
164
+ response="Tool failed to generate a response.", source_nodes=[]
165
+ )
166
+
167
+ # Extract citation metadata
168
+ pattern = r"\[\[(\d+)\]" if include_citations else r"\[(\d+)\]"
169
+ matches = re.findall(pattern, response.response)
170
+ citation_numbers = [int(match) for match in matches]
171
+ citation_metadata: dict = {
172
+ f"metadata for citation {citation_number}": response.source_nodes[
173
+ citation_number - 1
174
+ ].metadata
175
+ for citation_number in citation_numbers
176
+ }
177
+ res = {
178
+ "response": response.response,
179
+ "citation_metadata": citation_metadata,
180
+ "factual_consistency": (
181
+ response.metadata["fcs"] if "fcs" in response.metadata else 0.0
182
+ ),
183
+ }
184
+ return res
185
+
186
+ # Create signature for rag_function based on tool_args_schema
187
+ parameters = []
188
+ for name, param in tool_args_schema.__fields__.items():
189
+ default = inspect.Parameter.empty
190
+ if param.default is not None:
191
+ default = param.default
192
+ elif param.default_factory is not None:
193
+ default = param.default_factory()
194
+
195
+ parameters.append(
196
+ inspect.Parameter(
197
+ name,
198
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
199
+ default=default,
200
+ annotation=param.type_ if param.required else Optional[param.type_],
201
+ )
202
+ )
203
+ if (
204
+ "query" not in tool_args_schema.__fields__
205
+ ): # Add 'query' parameter if it's not already in the schema
206
+ parameters.append(
207
+ inspect.Parameter(
208
+ "query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str
209
+ )
210
+ )
211
+ new_signature = inspect.Signature(parameters, return_annotation=dict[str, Any])
212
+ setattr(rag_function, "__signature__", new_signature)
213
+
214
+ # Set the function name and docstring
215
+ doc_string = f"{tool_description}\n\n"
216
+ doc_string += "Parameters:\n"
217
+ for field_name, field in tool_args_schema.__fields__.items():
218
+ type_name = field.type_.__name__
219
+ if field.allow_none:
220
+ type_name = f"Optional[{type_name}]"
221
+
222
+ default_info = ""
223
+ if field.default is not None:
224
+ default_info = f" (default: {field.default})"
225
+ elif field.default_factory is not None:
226
+ default_info = " (default: set by factory)"
227
+
228
+ doc_string += f"- {field_name} ({type_name}): {field.field_info.description}{default_info}\n"
229
+
230
+ doc_string += "\nReturns: a dict[str, Any] with the following:\n"
231
+ doc_string += (
232
+ "- response: The response string in markdown format with citations.\n"
233
+ )
234
+ doc_string += "- citation_metadata: a dictionary of metadata for each citation included in the response string.\n"
235
+ doc_string += "- response_factual_consistency: a value representing confidence in the response being factually correct (1.0=high, 0.0=low).\n"
236
+
237
+ rag_function.__name__ = "_" + re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
238
+ rag_function.__doc__ = doc_string
239
+
240
+ # Create the tool
241
+ tool = FunctionTool.from_defaults(
242
+ fn=rag_function,
243
+ name=tool_name,
244
+ description=doc_string,
245
+ fn_schema=tool_args_schema,
246
+ )
247
+ return VectaraTool(tool, ToolType.QUERY)
248
+
249
+
250
+ class ToolsFactory:
251
+ """
252
+ A factory class for creating agent tools
253
+ """
254
+
255
+ def create_tool(
256
+ self, function: Callable, tool_type: ToolType = ToolType.QUERY
257
+ ) -> List[FunctionTool]:
258
+ """
259
+ Create a tool from a function.
260
+
261
+ Parameters:
262
+ - function (Callable): a function to convert into a tool.
263
+ - tool_type (ToolType): the type of tool.
264
+ """
265
+ return VectaraTool(FunctionTool.from_defaults(function), tool_type)
266
+
267
+ def get_llama_index_tools(
268
+ self,
269
+ tool_package_name: str,
270
+ tool_spec_name: str,
271
+ tool_name_prefix: str = "",
272
+ **kwargs: dict,
273
+ ) -> List[FunctionTool]:
274
+ """
275
+ Get a tool from the llama_index hub.
276
+
277
+ Parameters:
278
+ - tool_package_name (str): The name of the tool package.
279
+ - tool_spec_name (str): The name of the tool spec.
280
+ - tool_name_prefix (str): The prefix to add to the tool names (added to every tool in the spec).
281
+ - kwargs (dict): The keyword arguments to pass to the tool constructor (see Hub for tool specific details).
282
+ """
283
+ # Dynamically install and import the module
284
+ if tool_package_name not in LI_packages.keys():
285
+ raise ValueError(
286
+ f"Tool package {tool_package_name} from LlamaIndex not supported by Vectara-agentic."
287
+ )
288
+
289
+ module_name = f"llama_index.tools.{tool_package_name}"
290
+ module = importlib.import_module(module_name)
291
+
292
+ # Get the tool spec class or function from the module
293
+ tool_spec = getattr(module, tool_spec_name)
294
+
295
+ func_type = LI_packages[tool_package_name]
296
+ tools = tool_spec(**kwargs).to_tool_list()
297
+ vtools = []
298
+ for tool in tools:
299
+ if len(tool_name_prefix) > 0:
300
+ tool._metadata.name = tool_name_prefix + "_" + tool._metadata.name
301
+ if isinstance(func_type, dict):
302
+ if tool_spec_name not in func_type.keys():
303
+ raise ValueError(
304
+ f"Tool spec {tool_spec_name} not found in package {tool_package_name}."
305
+ )
306
+ tool_type = func_type[tool_spec_name]
307
+ else:
308
+ tool_type = func_type
309
+ vtools.append(VectaraTool(tool, tool_type))
310
+
311
+ return vtools
312
+
313
+ def standard_tools(self) -> List[FunctionTool]:
314
+ """
315
+ Create a list of standard tools.
316
+ """
317
+ return [self.create_tool(tool) for tool in [summarize_text, rephrase_text]]
318
+
319
+ def guardrail_tools(self) -> List[FunctionTool]:
320
+ return [
321
+ self.create_tool(tool)
322
+ for tool in [guardrails_no_politics, guardrails_be_polite]
323
+ ]
324
+
325
+ def financial_tools(self):
326
+ """
327
+ Create a list of financial tools.
328
+ """
329
+ return self.get_llama_index_tools("yahoo_finance", "YahooFinanceToolSpec")
330
+
331
+ def legal_tools(self) -> List[FunctionTool]:
332
+ def summarize_legal_text(
333
+ text: str = Field(description="the original text."),
334
+ ) -> str:
335
+ """
336
+ Use this tool to summarize legal text with no more than summary_max_length characters.
337
+ """
338
+ return summarize_text(text, expertise="law")
339
+
340
+ def critique_as_judge(
341
+ text: str = Field(description="the original text."),
342
+ ) -> str:
343
+ """
344
+ critique the legal document.
345
+ """
346
+ return critique_text(
347
+ text,
348
+ role="judge",
349
+ point_of_view="""
350
+ an experienced judge evaluating a legal document to provide areas of concern
351
+ or that may require further legal scrutiny or legal argument.
352
+ """,
353
+ )
354
+
355
+ return [
356
+ self.create_tool(tool) for tool in [summarize_legal_text, critique_as_judge]
357
+ ]
358
+
359
+ def database_tools(
360
+ self,
361
+ tool_name_prefix: str = "",
362
+ content_description: Optional[str] = None,
363
+ sql_database: Optional[SQLDatabase] = None,
364
+ scheme: Optional[str] = None,
365
+ host: str = "localhost",
366
+ port: str = "5432",
367
+ user: str = "postgres",
368
+ password: str = "Password",
369
+ dbname: str = "postgres",
370
+ ) -> List[FunctionTool]:
371
+ """
372
+ returns a list of database tools.
373
+ Args:
374
+ - tool_name_prefix (str, optional): The prefix to add to the tool names. Defaults to "".
375
+ - content_description (str, optional): The content description for the database. Defaults to None.
376
+ - sql_database (SQLDatabase, optional): The SQLDatabase object. Defaults to None.
377
+ - scheme (str, optional): The database scheme. Defaults to None.
378
+ - host (str, optional): The database host. Defaults to "localhost".
379
+ - port (str, optional): The database port. Defaults to "5432".
380
+ - user (str, optional): The database user. Defaults to "postgres".
381
+ - password (str, optional): The database password. Defaults to "Password".
382
+ - dbname (str, optional): The database name. Defaults to "postgres".
383
+ You must specify either the sql_database object or the scheme, host, port, user, password, and dbname.
384
+ """
385
+ if sql_database:
386
+ tools = self.get_llama_index_tools(
387
+ "database",
388
+ "DatabaseToolSpec",
389
+ tool_name_prefix=tool_name_prefix,
390
+ sql_database=sql_database,
391
+ )
392
+ else:
393
+ if scheme in ["postgresql", "mysql", "sqlite", "mssql", "oracle"]:
394
+ tools = self.get_llama_index_tools(
395
+ "database",
396
+ "DatabaseToolSpec",
397
+ tool_name_prefix=tool_name_prefix,
398
+ scheme=scheme,
399
+ host=host,
400
+ port=port,
401
+ user=user,
402
+ password=password,
403
+ dbname=dbname,
404
+ )
405
+ else:
406
+ raise "Please provide a SqlDatabase option or a valid DB scheme type (postgresql, mysql, sqlite, mssql, oracle)."
407
+
408
+ # Update tools with description
409
+ for tool in tools:
410
+ if content_description:
411
+ tool._metadata.description = (
412
+ tool._metadata.description
413
+ + f"The database tables include data about {content_description}."
414
+ )
415
+ return tools
@@ -0,0 +1,112 @@
1
+ """
2
+ This module contains the tools catalog for the Vectara Agentic.
3
+ """
4
+
5
+ from typing import Optional
6
+ from pydantic import Field
7
+ import requests
8
+
9
+ from .types import LLMRole
10
+ from .utils import get_llm
11
+
12
+ req_session = requests.Session()
13
+
14
+ get_headers = {
15
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0",
16
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
17
+ "Accept-Language": "en-US,en;q=0.5",
18
+ "Accept-Encoding": "gzip, deflate",
19
+ "Connection": "keep-alive",
20
+ }
21
+
22
+ #
23
+ # Standard Tools
24
+ #
25
+
26
+ def summarize_text(
27
+ text: str = Field(description="the original text."),
28
+ expertise: str = Field(
29
+ description="the expertise to apply to the summarization.",
30
+ ),
31
+ ) -> str:
32
+ """
33
+ This is a helper tool. It does not provide new information.
34
+ Use this tool to summarize text with no more than summary_max_length
35
+ characters.
36
+ """
37
+ expertise = "general" if len(expertise) < 3 else expertise.lower()
38
+ prompt = f"As an expert in {expertise}, summarize the provided text"
39
+ prompt += " into a concise summary."
40
+ prompt += f"\noriginal text: {text}\nsummary:"
41
+ llm = get_llm(LLMRole.TOOL)
42
+ response = llm.complete(prompt)
43
+ return response.text
44
+
45
+
46
+ def rephrase_text(
47
+ text: str = Field(description="the original text."),
48
+ instructions: str = Field(
49
+ description="the specific instructions for how to rephrase the text."
50
+ ),
51
+ ) -> str:
52
+ """
53
+ This is a helper tool. It does not provide new information.
54
+ Use this tool to rephrase the text according to the provided instructions.
55
+ For example, instructions could be "as a 5 year old would say it."
56
+ """
57
+ prompt = f"""
58
+ Rephrase the provided text according to the following instructions: {instructions}.
59
+ If the input is Markdown, keep the output in Markdown as well.
60
+ original text: {text}
61
+ rephrased text:
62
+ """
63
+ llm = get_llm(LLMRole.TOOL)
64
+ response = llm.complete(prompt)
65
+ return response.text
66
+
67
+
68
+ def critique_text(
69
+ text: str = Field(description="the original text."),
70
+ role: Optional[str] = Field(
71
+ None, description="the role of the person providing critique."
72
+ ),
73
+ point_of_view: Optional[str] = Field(
74
+ None, description="the point of view with which to provide critique."
75
+ ),
76
+ ) -> str:
77
+ """
78
+ This is a helper tool. It does not provide new information.
79
+ Critique the text from the specified point of view.
80
+ """
81
+ if role:
82
+ prompt = f"As a {role}, critique the provided text from the point of view of {point_of_view}."
83
+ else:
84
+ prompt = (
85
+ f"Critique the provided text from the point of view of {point_of_view}."
86
+ )
87
+ prompt += "Structure the critique as bullet points.\n"
88
+ prompt += f"Original text: {text}\nCritique:"
89
+ llm = get_llm(LLMRole.TOOL)
90
+ response = llm.complete(prompt)
91
+ return response.text
92
+
93
+
94
+ #
95
+ # Guardrails tools
96
+ #
97
+
98
+
99
+ def guardrails_no_politics(text: str = Field(description="the original text.")) -> str:
100
+ """
101
+ A guardrails tool.
102
+ Can be used to rephrase text so that it does not have political content.
103
+ """
104
+ return rephrase_text(text, "avoid any specific political content.")
105
+
106
+
107
+ def guardrails_be_polite(text: str = Field(description="the original text.")) -> str:
108
+ """
109
+ A guardrails tool.
110
+ Can be used to rephrase the text so that the response is in a polite tone.
111
+ """
112
+ return rephrase_text(text, "Ensure the response is super polite.")
@@ -0,0 +1,41 @@
1
+ """
2
+ This module contains the types used in the Vectara Agentic.
3
+ """
4
+
5
+ from enum import Enum
6
+
7
+ class AgentType(Enum):
8
+ """Enumeration for different types of agents."""
9
+
10
+ REACT = "REACT"
11
+ OPENAI = "OPENAI"
12
+
13
+
14
+ class ModelProvider(Enum):
15
+ """Enumeration for different types of model providers."""
16
+
17
+ OPENAI = "OPENAI"
18
+ ANTHROPIC = "ANTHROPIC"
19
+ TOGETHER = "TOGETHER"
20
+ GROQ = "GROQ"
21
+ FIREWORKS = "FIREWORKS"
22
+
23
+
24
+ class AgentStatusType(Enum):
25
+ """Enumeration for different types of agent statuses."""
26
+
27
+ AGENT_UPDATE = "agent_update"
28
+ TOOL_CALL = "tool_call"
29
+ TOOL_OUTPUT = "tool_output"
30
+
31
+
32
+ class LLMRole(Enum):
33
+ """Enumeration for different types of LLM roles."""
34
+
35
+ MAIN: str = "MAIN"
36
+ TOOL: str = "TOOL"
37
+
38
+
39
+ class ToolType(Enum):
40
+ QUERY = "query"
41
+ ACTION = "action"
@@ -0,0 +1,69 @@
1
+ """
2
+ Utilities for the Vectara agentic.
3
+ """
4
+
5
+ import os
6
+
7
+ from llama_index.core.llms import LLM
8
+ from llama_index.llms.openai import OpenAI
9
+ from llama_index.llms.anthropic import Anthropic
10
+ from llama_index.llms.together import TogetherLLM
11
+ from llama_index.llms.groq import Groq
12
+ from llama_index.llms.fireworks import Fireworks
13
+
14
+ from .types import LLMRole, AgentType, ModelProvider
15
+
16
+ provider_to_default_model_name = {
17
+ ModelProvider.OPENAI: "gpt-4o",
18
+ ModelProvider.ANTHROPIC: "claude-3-5-sonnet-20240620",
19
+ ModelProvider.TOGETHER: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
20
+ ModelProvider.GROQ: "mixtral-8x7b-32768",
21
+ ModelProvider.FIREWORKS: "accounts/fireworks/models/firefunction-v2",
22
+ }
23
+
24
+ DEFAULT_MODEL_PROVIDER = ModelProvider.OPENAI
25
+
26
+
27
+ def get_llm(role: LLMRole) -> LLM:
28
+ """Get the LLM for the specified role."""
29
+ agent_type = AgentType(os.getenv("VECTARA_AGENTIC_AGENT_TYPE", AgentType.OPENAI))
30
+ if role == LLMRole.TOOL:
31
+ model_provider = ModelProvider(
32
+ os.getenv("VECTARA_AGENTIC_TOOL_LLM_PROVIDER", DEFAULT_MODEL_PROVIDER)
33
+ )
34
+ model_name = os.getenv(
35
+ "VECTARA_AGENTIC_TOOL_MODEL_NAME",
36
+ provider_to_default_model_name.get(model_provider),
37
+ )
38
+ else:
39
+ model_provider = ModelProvider(
40
+ os.getenv("VECTARA_AGENTIC_MAIN_LLM_PROVIDER", DEFAULT_MODEL_PROVIDER)
41
+ )
42
+ model_name = os.getenv(
43
+ "VECTARA_AGENTIC_MAIN_MODEL_NAME",
44
+ provider_to_default_model_name.get(model_provider),
45
+ )
46
+
47
+ if (
48
+ role == LLMRole.MAIN
49
+ and agent_type == AgentType.OPENAI
50
+ and model_provider != ModelProvider.OPENAI
51
+ ):
52
+ raise ValueError(
53
+ "OpenAI agent requested but main model provider is not OpenAI."
54
+ )
55
+
56
+ if model_provider == ModelProvider.OPENAI:
57
+ llm = OpenAI(model=model_name, temperature=0)
58
+ elif model_provider == ModelProvider.ANTHROPIC:
59
+ llm = Anthropic(model=model_name, temperature=0)
60
+ elif model_provider == ModelProvider.TOGETHER:
61
+ llm = TogetherLLM(model=model_name, temperature=0)
62
+ elif model_provider == ModelProvider.GROQ:
63
+ llm = Groq(model=model_name, temperature=0)
64
+ elif model_provider == ModelProvider.FIREWORKS:
65
+ llm = Fireworks(model=model_name, temperature=0)
66
+ else:
67
+ raise ValueError(f"Unknown LLM provider: {model_provider}")
68
+
69
+ return llm