speedy-utils 1.1.16__tar.gz → 1.1.18__tar.gz

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.
Files changed (52) hide show
  1. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/PKG-INFO +3 -2
  2. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/pyproject.toml +1 -1
  3. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/__init__.py +8 -1
  4. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/chat_format/display.py +109 -14
  5. speedy_utils-1.1.18/src/llm_utils/lm/__init__.py +13 -0
  6. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/lm/async_lm/async_llm_task.py +0 -12
  7. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/lm/async_lm/async_lm.py +13 -4
  8. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/lm/async_lm/async_lm_base.py +24 -14
  9. speedy_utils-1.1.18/src/llm_utils/lm/base_prompt_builder.py +288 -0
  10. speedy_utils-1.1.18/src/llm_utils/lm/llm_task.py +400 -0
  11. speedy_utils-1.1.18/src/llm_utils/lm/lm.py +207 -0
  12. speedy_utils-1.1.18/src/llm_utils/lm/lm_base.py +285 -0
  13. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/vector_cache/core.py +297 -87
  14. speedy_utils-1.1.18/src/speedy_utils/common/patcher.py +68 -0
  15. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/utils_cache.py +5 -5
  16. speedy_utils-1.1.18/src/speedy_utils/common/utils_io.py +398 -0
  17. speedy_utils-1.1.18/src/speedy_utils/multi_worker/process.py +134 -0
  18. speedy_utils-1.1.16/src/llm_utils/lm/__init__.py +0 -12
  19. speedy_utils-1.1.16/src/speedy_utils/common/utils_io.py +0 -172
  20. speedy_utils-1.1.16/src/speedy_utils/multi_worker/process.py +0 -203
  21. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/README.md +0 -0
  22. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/chat_format/__init__.py +0 -0
  23. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/chat_format/transform.py +0 -0
  24. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/chat_format/utils.py +0 -0
  25. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/group_messages.py +0 -0
  26. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/lm/async_lm/__init__.py +0 -0
  27. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/lm/async_lm/_utils.py +0 -0
  28. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/lm/async_lm/lm_specific.py +0 -0
  29. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/lm/openai_memoize.py +0 -0
  30. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/lm/utils.py +0 -0
  31. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/scripts/README.md +0 -0
  32. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/scripts/vllm_load_balancer.py +0 -0
  33. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/scripts/vllm_serve.py +0 -0
  34. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/vector_cache/__init__.py +0 -0
  35. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/vector_cache/cli.py +0 -0
  36. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/vector_cache/types.py +0 -0
  37. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/llm_utils/vector_cache/utils.py +0 -0
  38. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/__init__.py +0 -0
  39. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/all.py +0 -0
  40. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/__init__.py +0 -0
  41. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/clock.py +0 -0
  42. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/function_decorator.py +0 -0
  43. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/logger.py +0 -0
  44. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/notebook_utils.py +0 -0
  45. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/report_manager.py +0 -0
  46. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/utils_misc.py +0 -0
  47. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/common/utils_print.py +0 -0
  48. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/multi_worker/__init__.py +0 -0
  49. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/multi_worker/thread.py +0 -0
  50. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/scripts/__init__.py +0 -0
  51. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/scripts/mpython.py +0 -0
  52. {speedy_utils-1.1.16 → speedy_utils-1.1.18}/src/speedy_utils/scripts/openapi_client_codegen.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: speedy-utils
3
- Version: 1.1.16
3
+ Version: 1.1.18
4
4
  Summary: Fast and easy-to-use package for data science
5
5
  Author: AnhVTH
6
6
  Author-email: anhvth.226@gmail.com
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
15
16
  Requires-Dist: bump2version
16
17
  Requires-Dist: cachetools
17
18
  Requires-Dist: debugpy
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "speedy-utils"
3
- version = "1.1.16"
3
+ version = "1.1.18"
4
4
  description = "Fast and easy-to-use package for data science"
5
5
  authors = ["AnhVTH <anhvth.226@gmail.com>"]
6
6
  readme = "README.md"
@@ -1,5 +1,10 @@
1
1
  from llm_utils.lm.openai_memoize import MOpenAI
2
+ from llm_utils.lm import LLMTask, AsyncLM, AsyncLLMTask
2
3
  from llm_utils.vector_cache import VectorCache
4
+ from llm_utils.lm.lm_base import get_model_name
5
+ from llm_utils.lm.base_prompt_builder import BasePromptBuilder
6
+
7
+
3
8
 
4
9
  from .chat_format import (
5
10
  build_chatml_input,
@@ -12,7 +17,6 @@ from .chat_format import (
12
17
  transform_messages,
13
18
  transform_messages_to_chatml,
14
19
  )
15
- from .lm.async_lm import AsyncLLMTask, AsyncLM
16
20
 
17
21
  __all__ = [
18
22
  "transform_messages",
@@ -26,6 +30,9 @@ __all__ = [
26
30
  "display_chat_messages_as_html",
27
31
  "AsyncLM",
28
32
  "AsyncLLMTask",
33
+ "LLMTask",
29
34
  "MOpenAI",
35
+ "get_model_name",
30
36
  "VectorCache",
37
+ "BasePromptBuilder"
31
38
  ]
@@ -1,19 +1,93 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
3
4
  from difflib import SequenceMatcher
4
5
  from typing import Any, Optional
5
6
 
6
7
  from IPython.display import HTML, display
7
8
 
8
9
 
10
+ def _preprocess_as_json(content: str) -> str:
11
+ """
12
+ Preprocess content as JSON with proper formatting and syntax highlighting.
13
+ """
14
+ try:
15
+ # Try to parse and reformat JSON
16
+ parsed = json.loads(content)
17
+ return json.dumps(parsed, indent=2, ensure_ascii=False)
18
+ except (json.JSONDecodeError, TypeError):
19
+ # If not valid JSON, return as-is
20
+ return content
21
+
22
+
23
+ def _preprocess_as_markdown(content: str) -> str:
24
+ """
25
+ Preprocess content as markdown with proper formatting.
26
+ """
27
+ # Basic markdown preprocessing - convert common patterns
28
+ lines = content.split('\n')
29
+ processed_lines = []
30
+
31
+ for line in lines:
32
+ # Convert **bold** to span with bold styling
33
+ while '**' in line:
34
+ first_pos = line.find('**')
35
+ if first_pos != -1:
36
+ second_pos = line.find('**', first_pos + 2)
37
+ if second_pos != -1:
38
+ before = line[:first_pos]
39
+ bold_text = line[first_pos + 2:second_pos]
40
+ after = line[second_pos + 2:]
41
+ line = f'{before}<span style="font-weight: bold;">{bold_text}</span>{after}'
42
+ else:
43
+ break
44
+ else:
45
+ break
46
+
47
+ # Convert *italic* to span with italic styling
48
+ while '*' in line and line.count('*') >= 2:
49
+ first_pos = line.find('*')
50
+ if first_pos != -1:
51
+ second_pos = line.find('*', first_pos + 1)
52
+ if second_pos != -1:
53
+ before = line[:first_pos]
54
+ italic_text = line[first_pos + 1:second_pos]
55
+ after = line[second_pos + 1:]
56
+ line = f'{before}<span style="font-style: italic;">{italic_text}</span>{after}'
57
+ else:
58
+ break
59
+ else:
60
+ break
61
+
62
+ # Convert # headers to bold headers
63
+ if line.strip().startswith('#'):
64
+ level = len(line) - len(line.lstrip('#'))
65
+ header_text = line.lstrip('# ').strip()
66
+ line = f'<span style="font-weight: bold; font-size: 1.{min(4, level)}em;">{header_text}</span>'
67
+
68
+ processed_lines.append(line)
69
+
70
+ return '\n'.join(processed_lines)
71
+
72
+
9
73
  def show_chat(
10
74
  msgs: Any,
11
75
  return_html: bool = False,
12
76
  file: str = "/tmp/conversation.html",
13
77
  theme: str = "default",
78
+ as_markdown: bool = False,
79
+ as_json: bool = False,
14
80
  ) -> Optional[str]:
15
81
  """
16
82
  Display chat messages as HTML.
83
+
84
+ Args:
85
+ msgs: Chat messages in various formats
86
+ return_html: If True, return HTML string instead of displaying
87
+ file: Path to save HTML file
88
+ theme: Color theme ('default', 'light', 'dark')
89
+ as_markdown: If True, preprocess content as markdown
90
+ as_json: If True, preprocess content as JSON
17
91
  """
18
92
  if isinstance(msgs, dict) and "messages" in msgs:
19
93
  msgs = msgs["messages"]
@@ -74,45 +148,66 @@ def show_chat(
74
148
  name = tool_call["name"]
75
149
  args = tool_call["arguments"]
76
150
  content += f"Tool: {name}\nArguments: {args}"
77
- content = content.replace("\n", "<br>")
78
- content = content.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
79
- content = content.replace(" ", "&nbsp;&nbsp;")
80
- content = (
81
- content.replace("<br>", "TEMP_BR")
82
- .replace("<", "&lt;")
83
- .replace(">", "&gt;")
84
- .replace("TEMP_BR", "<br>")
85
- )
151
+
152
+ # Preprocess content based on format options
153
+ if as_json:
154
+ content = _preprocess_as_json(content)
155
+ elif as_markdown:
156
+ content = _preprocess_as_markdown(content)
157
+
158
+ # Handle HTML escaping differently for markdown vs regular content
159
+ if as_markdown:
160
+ # For markdown, preserve HTML tags but escape other characters carefully
161
+ content = content.replace("\n", "<br>")
162
+ content = content.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
163
+ content = content.replace(" ", "&nbsp;&nbsp;")
164
+ # Don't escape < and > for markdown since we want to preserve our span tags
165
+ else:
166
+ # Regular escaping for non-markdown content
167
+ content = content.replace("\n", "<br>")
168
+ content = content.replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;")
169
+ content = content.replace(" ", "&nbsp;&nbsp;")
170
+ content = (
171
+ content.replace("<br>", "TEMP_BR")
172
+ .replace("<", "&lt;")
173
+ .replace(">", "&gt;")
174
+ .replace("TEMP_BR", "<br>")
175
+ )
86
176
  if role in color_scheme:
87
177
  background_color = color_scheme[role]["background"]
88
178
  text_color = color_scheme[role]["text"]
89
179
  else:
90
180
  background_color = color_scheme["default"]["background"]
91
181
  text_color = color_scheme["default"]["text"]
182
+
183
+ # Choose container based on whether we have markdown formatting
184
+ content_container = "div" if as_markdown else "pre"
185
+ container_style = 'style="white-space: pre-wrap;"' if as_markdown else ""
186
+
92
187
  if role == "system":
93
188
  conversation_html += (
94
189
  f'<div style="background-color: {background_color}; color: {text_color}; padding: 10px; margin-bottom: 10px;">'
95
- f'<strong>System:</strong><br><pre id="system-{i}">{content}</pre></div>'
190
+ f'<strong>System:</strong><br><{content_container} id="system-{i}" {container_style}>{content}</{content_container}></div>'
96
191
  )
97
192
  elif role == "user":
98
193
  conversation_html += (
99
194
  f'<div style="background-color: {background_color}; color: {text_color}; padding: 10px; margin-bottom: 10px;">'
100
- f'<strong>User:</strong><br><pre id="user-{i}">{content}</pre></div>'
195
+ f'<strong>User:</strong><br><{content_container} id="user-{i}" {container_style}>{content}</{content_container}></div>'
101
196
  )
102
197
  elif role == "assistant":
103
198
  conversation_html += (
104
199
  f'<div style="background-color: {background_color}; color: {text_color}; padding: 10px; margin-bottom: 10px;">'
105
- f'<strong>Assistant:</strong><br><pre id="assistant-{i}">{content}</pre></div>'
200
+ f'<strong>Assistant:</strong><br><{content_container} id="assistant-{i}" {container_style}>{content}</{content_container}></div>'
106
201
  )
107
202
  elif role == "function":
108
203
  conversation_html += (
109
204
  f'<div style="background-color: {background_color}; color: {text_color}; padding: 10px; margin-bottom: 10px;">'
110
- f'<strong>Function:</strong><br><pre id="function-{i}">{content}</pre></div>'
205
+ f'<strong>Function:</strong><br><{content_container} id="function-{i}" {container_style}>{content}</{content_container}></div>'
111
206
  )
112
207
  else:
113
208
  conversation_html += (
114
209
  f'<div style="background-color: {background_color}; color: {text_color}; padding: 10px; margin-bottom: 10px;">'
115
- f'<strong>{role}:</strong><br><pre id="{role}-{i}">{content}</pre><br>'
210
+ f'<strong>{role}:</strong><br><{content_container} id="{role}-{i}" {container_style}>{content}</{content_container}><br>'
116
211
  f"<button onclick=\"copyContent('{role}-{i}')\">Copy</button></div>"
117
212
  )
118
213
  html: str = f"""
@@ -0,0 +1,13 @@
1
+ from .async_lm.async_lm import AsyncLM
2
+ from .async_lm.async_llm_task import AsyncLLMTask
3
+ from .lm_base import LMBase, get_model_name
4
+ from .llm_task import LLMTask
5
+ from .base_prompt_builder import BasePromptBuilder
6
+
7
+ __all__ = [
8
+ "LMBase",
9
+ "LLMTask",
10
+ "AsyncLM",
11
+ "AsyncLLMTask",
12
+ "BasePromptBuilder",
13
+ ]
@@ -11,8 +11,6 @@ from venv import logger
11
11
 
12
12
  from openai.types.chat import ChatCompletionMessageParam
13
13
  from pydantic import BaseModel
14
- from pytest import Cache
15
- from speedy_utils import jdumps
16
14
  from speedy_utils.all import dump_json_or_pickle, identify
17
15
 
18
16
  from llm_utils.chat_format.display import get_conversation_one_turn
@@ -35,8 +33,6 @@ class LMConfiguration:
35
33
  model: Optional[str] = None
36
34
  temperature: Optional[float] = None
37
35
  max_tokens: Optional[int] = None
38
- host: Optional[str] = None
39
- port: Optional[Union[int, str]] = None
40
36
  base_url: Optional[str] = None
41
37
  api_key: Optional[str] = None
42
38
  cache: Optional[bool] = True
@@ -55,8 +51,6 @@ class LMConfiguration:
55
51
  "model": self.model,
56
52
  "temperature": self.temperature,
57
53
  "max_tokens": self.max_tokens,
58
- "host": self.host,
59
- "port": self.port,
60
54
  "base_url": self.base_url,
61
55
  "api_key": self.api_key,
62
56
  "cache": self.cache,
@@ -92,8 +86,6 @@ class AsyncLLMTask(ABC, Generic[InputModelType, OutputModelType]):
92
86
  DEFAULT_CACHE_DIR: Optional[pathlib.Path] = None
93
87
  DEFAULT_TEMPERATURE: Optional[float] = None
94
88
  DEFAULT_MAX_TOKENS: Optional[int] = None
95
- DEFAULT_HOST: Optional[str] = None
96
- DEFAULT_PORT: Optional[Union[int, str]] = None
97
89
  DEFAULT_TOP_P: Optional[float] = None
98
90
  DEFAULT_PRESENCE_PENALTY: Optional[float] = None
99
91
  DEFAULT_TOP_K: Optional[int] = None
@@ -114,8 +106,6 @@ class AsyncLLMTask(ABC, Generic[InputModelType, OutputModelType]):
114
106
  model: Optional[str] = None,
115
107
  temperature: Optional[float] = None,
116
108
  max_tokens: Optional[int] = None,
117
- host: Optional[str] = None,
118
- port: Optional[Union[int, str]] = None,
119
109
  base_url: Optional[str] = None,
120
110
  api_key: Optional[str] = None,
121
111
  cache: Optional[bool] = None,
@@ -141,8 +131,6 @@ class AsyncLLMTask(ABC, Generic[InputModelType, OutputModelType]):
141
131
  max_tokens=max_tokens
142
132
  if max_tokens is not None
143
133
  else self.DEFAULT_MAX_TOKENS,
144
- host=host if host is not None else self.DEFAULT_HOST,
145
- port=port if port is not None else self.DEFAULT_PORT,
146
134
  base_url=base_url if base_url is not None else self.DEFAULT_BASE_URL,
147
135
  api_key=api_key if api_key is not None else self.DEFAULT_API_KEY,
148
136
  cache=cache if cache is not None else self.DEFAULT_CACHE,
@@ -27,7 +27,6 @@ from ._utils import (
27
27
 
28
28
 
29
29
  def jloads_safe(content: str) -> Any:
30
- # if contain ```json, remove it
31
30
  if "```json" in content:
32
31
  content = content.split("```json")[1].strip().split("```")[0].strip()
33
32
  try:
@@ -72,8 +71,6 @@ class AsyncLM(AsyncLMBase):
72
71
  print(f"Using model: {model}")
73
72
 
74
73
  super().__init__(
75
- host=host,
76
- port=port,
77
74
  ports=ports,
78
75
  base_url=base_url,
79
76
  cache=cache,
@@ -231,6 +228,7 @@ class AsyncLM(AsyncLMBase):
231
228
  def _extract_assistant_message(self, choice): # -> dict[str, str] | dict[str, Any]:
232
229
  # TODO this current assume choice is a dict with "reasoning_content" and "content"
233
230
  has_reasoning = False
231
+ reasoning_content = ""
234
232
  if "reasoning_content" in choice and isinstance(
235
233
  choice["reasoning_content"], str
236
234
  ):
@@ -249,7 +247,7 @@ class AsyncLM(AsyncLMBase):
249
247
 
250
248
  return assistant_msg
251
249
 
252
- async def __call__(
250
+ async def call_with_messages(
253
251
  self,
254
252
  prompt: Optional[str] = None,
255
253
  messages: Optional[RawMsgs] = None,
@@ -295,6 +293,17 @@ class AsyncLM(AsyncLMBase):
295
293
  msg_dump = dict(assistant_msg)
296
294
  return msg_dump, full_messages
297
295
 
296
+
297
+ def call_sync(
298
+ self,
299
+ prompt: Optional[str] = None,
300
+ messages: Optional[RawMsgs] = None,
301
+ max_tokens: Optional[int] = None,
302
+ ):
303
+ """Synchronous wrapper around the async __call__ method."""
304
+ import asyncio
305
+ return asyncio.run(self.__call__(prompt=prompt, messages=messages, max_tokens=max_tokens))
306
+
298
307
  async def parse(
299
308
  self,
300
309
  instruction,
@@ -40,32 +40,40 @@ class AsyncLMBase:
40
40
  def __init__(
41
41
  self,
42
42
  *,
43
- host: str = "localhost",
44
- port: Optional[Union[int, str]] = None,
45
43
  base_url: Optional[str] = None,
46
44
  api_key: Optional[str] = None,
47
45
  cache: bool = True,
48
46
  ports: Optional[List[int]] = None,
49
47
  ) -> None:
50
- self._port = port
51
- self._host = host
52
- self.base_url = base_url or (f"http://{host}:{port}/v1" if port else None)
48
+ self.base_url = base_url
53
49
  self.api_key = api_key or os.getenv("OPENAI_API_KEY", "abc")
54
50
  self._cache = cache
55
51
  self.ports = ports
56
- self._init_port = port # <-- store the port provided at init
57
52
 
58
53
  @property
59
54
  def client(self) -> MAsyncOpenAI:
60
55
  # if have multiple ports
61
- if self.ports:
56
+ if self.ports and self.base_url:
62
57
  import random
63
-
58
+ import re
59
+
64
60
  port = random.choice(self.ports)
65
- api_base = f"http://{self._host}:{port}/v1"
61
+ # Replace port in base_url if it exists
62
+ base_url_pattern = r'(https?://[^:/]+):?\d*(/.*)?'
63
+ match = re.match(base_url_pattern, self.base_url)
64
+ if match:
65
+ host_part = match.group(1)
66
+ path_part = match.group(2) or '/v1'
67
+ api_base = f"{host_part}:{port}{path_part}"
68
+ else:
69
+ api_base = self.base_url
66
70
  logger.debug(f"Using port: {port}")
67
71
  else:
68
- api_base = self.base_url or f"http://{self._host}:{self._port}/v1"
72
+ api_base = self.base_url
73
+
74
+ if api_base is None:
75
+ raise ValueError("base_url must be provided")
76
+
69
77
  client = MAsyncOpenAI(
70
78
  api_key=self.api_key,
71
79
  base_url=api_base,
@@ -182,11 +190,13 @@ class AsyncLMBase:
182
190
  # ------------------------------------------------------------------ #
183
191
 
184
192
  @staticmethod
185
- async def list_models(port=None, host="localhost") -> List[str]:
193
+ async def list_models(base_url: Optional[str] = None) -> List[str]:
186
194
  try:
187
- client = AsyncLMBase(port=port, host=host).client # type: ignore[arg-type]
188
- base_url: URL = client.base_url
189
- logger.debug(f"Base URL: {base_url}")
195
+ if base_url is None:
196
+ raise ValueError("base_url must be provided")
197
+ client = AsyncLMBase(base_url=base_url).client
198
+ base_url_obj: URL = client.base_url
199
+ logger.debug(f"Base URL: {base_url_obj}")
190
200
  models: AsyncSyncPage[Model] = await client.models.list() # type: ignore[assignment]
191
201
  return [model.id for model in models.data]
192
202
  except Exception as exc: