llmstudio 0.0.1b1__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.
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.1
2
+ Name: llmstudio
3
+ Version: 0.0.1b1
4
+ Summary: Prompt Perfection at Your Fingertips
5
+ Home-page: https://llmstudio.ai/
6
+ License: MIT
7
+ Keywords: ml,ai,llm,llmops,openai,langchain,chatgpt,llmstudio,tensorops
8
+ Author: Cláudio Lemos
9
+ Author-email: claudio@tensorops.ai
10
+ Requires-Python: >=3.9,<4.0
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Provides-Extra: proxy
18
+ Provides-Extra: tracker
19
+ Requires-Dist: langchain (>=0,<1)
20
+ Requires-Dist: langchain-experimental (>=0,<1)
21
+ Requires-Dist: llmstudio-core (==0.0.1b1)
22
+ Requires-Dist: llmstudio-proxy (==0.0.1b1) ; extra == "proxy"
23
+ Requires-Dist: llmstudio-tracker (==0.0.1b1) ; extra == "tracker"
24
+ Requires-Dist: pydantic (>=2.5,<3.0)
25
+ Requires-Dist: python-dotenv (>=0,<1)
26
+ Project-URL: Bug Tracker, https://github.com/tensoropsai/llmstudio/issues
27
+ Project-URL: Documentation, https://docs.llmstudio.ai
28
+ Project-URL: Repository, https://github.com/tensoropsai/llmstudio
29
+ Description-Content-Type: text/markdown
30
+
31
+ # LLMstudio by [TensorOps](http://tensorops.ai "TensorOps")
32
+
33
+ Prompt Engineering at your fingertips
34
+
35
+ ![LLMstudio logo](https://imgur.com/Xqsj6V2.gif)
36
+
37
+ ## 🌟 Features
38
+
39
+ ![LLMstudio UI](https://imgur.com/wrwiIUs.png)
40
+
41
+ - **LLM Proxy Access**: Seamless access to all the latest LLMs by OpenAI, Anthropic, Google.
42
+ - **Custom and Local LLM Support**: Use custom or local open-source LLMs through Ollama.
43
+ - **Prompt Playground UI**: A user-friendly interface for engineering and fine-tuning your prompts.
44
+ - **Python SDK**: Easily integrate LLMstudio into your existing workflows.
45
+ - **Monitoring and Logging**: Keep track of your usage and performance for all requests.
46
+ - **LangChain Integration**: LLMstudio integrates with your already existing LangChain projects.
47
+ - **Batch Calling**: Send multiple requests at once for improved efficiency.
48
+ - **Smart Routing and Fallback**: Ensure 24/7 availability by routing your requests to trusted LLMs.
49
+ - **Type Casting (soon)**: Convert data types as needed for your specific use case.
50
+
51
+ ## 🚀 Quickstart
52
+
53
+ Don't forget to check out [https://docs.llmstudio.ai](docs) page.
54
+
55
+ ## Installation
56
+
57
+ Install the latest version of **LLMstudio** using `pip`. We suggest that you create and activate a new environment using `conda`
58
+
59
+ ```bash
60
+ pip install llmstudio
61
+ ```
62
+
63
+ Install `bun` if you want to use the UI
64
+
65
+ ```bash
66
+ curl -fsSL https://bun.sh/install | bash
67
+ ```
68
+
69
+ Create a `.env` file at the same path you'll run **LLMstudio**
70
+
71
+ ```bash
72
+ OPENAI_API_KEY="sk-api_key"
73
+ ANTHROPIC_API_KEY="sk-api_key"
74
+ ```
75
+
76
+ Now you should be able to run **LLMstudio** using the following command.
77
+
78
+ ```bash
79
+ llmstudio server --ui
80
+ ```
81
+
82
+ When the `--ui` flag is set, you'll be able to access the UI at [http://localhost:3000](http://localhost:3000)
83
+
84
+ ## 📖 Documentation
85
+
86
+ - [Visit our docs to learn how the SDK works](https://docs.LLMstudio.ai) (coming soon)
87
+ - Checkout our [notebook examples](https://github.com/TensorOpsAI/LLMstudio/tree/main/examples) to follow along with interactive tutorials
88
+
89
+ ## 👨‍💻 Contributing
90
+
91
+ - Head on to our [Contribution Guide](https://github.com/TensorOpsAI/LLMstudio/tree/main/CONTRIBUTING.md) to see how you can help LLMstudio.
92
+ - Join our [Discord](https://discord.gg/GkAfPZR9wy) to talk with other LLMstudio enthusiasts.
93
+
94
+ ## Training
95
+
96
+ [![Banner](https://imgur.com/XTRFZ4m.png)](https://www.tensorops.ai/llm-studio-workshop)
97
+
98
+ ---
99
+
100
+ Thank you for choosing LLMstudio. Your journey to perfecting AI interactions starts here.
101
+
@@ -0,0 +1,70 @@
1
+ # LLMstudio by [TensorOps](http://tensorops.ai "TensorOps")
2
+
3
+ Prompt Engineering at your fingertips
4
+
5
+ ![LLMstudio logo](https://imgur.com/Xqsj6V2.gif)
6
+
7
+ ## 🌟 Features
8
+
9
+ ![LLMstudio UI](https://imgur.com/wrwiIUs.png)
10
+
11
+ - **LLM Proxy Access**: Seamless access to all the latest LLMs by OpenAI, Anthropic, Google.
12
+ - **Custom and Local LLM Support**: Use custom or local open-source LLMs through Ollama.
13
+ - **Prompt Playground UI**: A user-friendly interface for engineering and fine-tuning your prompts.
14
+ - **Python SDK**: Easily integrate LLMstudio into your existing workflows.
15
+ - **Monitoring and Logging**: Keep track of your usage and performance for all requests.
16
+ - **LangChain Integration**: LLMstudio integrates with your already existing LangChain projects.
17
+ - **Batch Calling**: Send multiple requests at once for improved efficiency.
18
+ - **Smart Routing and Fallback**: Ensure 24/7 availability by routing your requests to trusted LLMs.
19
+ - **Type Casting (soon)**: Convert data types as needed for your specific use case.
20
+
21
+ ## 🚀 Quickstart
22
+
23
+ Don't forget to check out [https://docs.llmstudio.ai](docs) page.
24
+
25
+ ## Installation
26
+
27
+ Install the latest version of **LLMstudio** using `pip`. We suggest that you create and activate a new environment using `conda`
28
+
29
+ ```bash
30
+ pip install llmstudio
31
+ ```
32
+
33
+ Install `bun` if you want to use the UI
34
+
35
+ ```bash
36
+ curl -fsSL https://bun.sh/install | bash
37
+ ```
38
+
39
+ Create a `.env` file at the same path you'll run **LLMstudio**
40
+
41
+ ```bash
42
+ OPENAI_API_KEY="sk-api_key"
43
+ ANTHROPIC_API_KEY="sk-api_key"
44
+ ```
45
+
46
+ Now you should be able to run **LLMstudio** using the following command.
47
+
48
+ ```bash
49
+ llmstudio server --ui
50
+ ```
51
+
52
+ When the `--ui` flag is set, you'll be able to access the UI at [http://localhost:3000](http://localhost:3000)
53
+
54
+ ## 📖 Documentation
55
+
56
+ - [Visit our docs to learn how the SDK works](https://docs.LLMstudio.ai) (coming soon)
57
+ - Checkout our [notebook examples](https://github.com/TensorOpsAI/LLMstudio/tree/main/examples) to follow along with interactive tutorials
58
+
59
+ ## 👨‍💻 Contributing
60
+
61
+ - Head on to our [Contribution Guide](https://github.com/TensorOpsAI/LLMstudio/tree/main/CONTRIBUTING.md) to see how you can help LLMstudio.
62
+ - Join our [Discord](https://discord.gg/GkAfPZR9wy) to talk with other LLMstudio enthusiasts.
63
+
64
+ ## Training
65
+
66
+ [![Banner](https://imgur.com/XTRFZ4m.png)](https://www.tensorops.ai/llm-studio-workshop)
67
+
68
+ ---
69
+
70
+ Thank you for choosing LLMstudio. Your journey to perfecting AI interactions starts here.
File without changes
@@ -0,0 +1,38 @@
1
+ import os
2
+ import signal
3
+ import threading
4
+
5
+ import click
6
+ from llmstudio.server import start_servers
7
+
8
+
9
+ def handle_shutdown(signum, frame):
10
+ print("Shutting down gracefully...")
11
+ os._exit(0)
12
+
13
+
14
+ @click.group()
15
+ def main():
16
+ pass
17
+
18
+
19
+ @main.command()
20
+ @click.option("--proxy", is_flag=True, help="Start the Proxy server.")
21
+ @click.option("--tracker", is_flag=True, help="Start the Tracker server.")
22
+ def server(proxy, tracker):
23
+ signal.signal(signal.SIGINT, handle_shutdown)
24
+
25
+ start_servers(proxy=proxy, tracker=tracker)
26
+
27
+ print("Servers are running. Press CTRL+C to stop.")
28
+
29
+ stop_event = threading.Event()
30
+ try:
31
+ stop_event.wait() # Wait indefinitely until the event is set
32
+ except KeyboardInterrupt:
33
+ print("Shutting down servers...")
34
+
35
+
36
+ if __name__ == "__main__":
37
+ # main()
38
+ server()
@@ -0,0 +1,137 @@
1
+ from typing import (
2
+ Any,
3
+ Callable,
4
+ Dict,
5
+ List,
6
+ Literal,
7
+ Optional,
8
+ Sequence,
9
+ Tuple,
10
+ Type,
11
+ Union,
12
+ )
13
+
14
+ from langchain.schema.messages import BaseMessage
15
+ from langchain.schema.output import ChatGeneration, ChatResult
16
+ from langchain_community.adapters.openai import (
17
+ convert_dict_to_message,
18
+ convert_message_to_dict,
19
+ )
20
+ from langchain_core.language_models.base import LanguageModelInput
21
+ from langchain_core.language_models.chat_models import BaseChatModel
22
+ from langchain_core.runnables import Runnable
23
+ from langchain_core.tools import BaseTool
24
+ from langchain_core.utils.function_calling import convert_to_openai_tool
25
+ from llmstudio.providers import LLM
26
+ from openai import BaseModel
27
+
28
+
29
+ class ChatLLMstudio(BaseChatModel):
30
+ llm: LLM
31
+ model: str
32
+ is_stream: bool = False
33
+ retries: int = 0
34
+ parameters: dict = {}
35
+
36
+ @property
37
+ def _llm_type(self):
38
+ return "LLMstudio"
39
+
40
+ def _create_message_dicts(
41
+ self, messages: List[BaseMessage], stop: Optional[List[str]]
42
+ ) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]:
43
+ message_dicts = [convert_message_to_dict(m) for m in messages]
44
+ return message_dicts
45
+
46
+ def _create_chat_result(self, response: Any) -> ChatResult:
47
+ generations = []
48
+ if not isinstance(response, dict):
49
+ response = response.dict()
50
+ for res in response["choices"]:
51
+ message = convert_dict_to_message(res["message"])
52
+ generation_info = dict(finish_reason=res.get("finish_reason"))
53
+ if "logprobs" in res:
54
+ generation_info["logprobs"] = res["logprobs"]
55
+ gen = ChatGeneration(
56
+ message=message,
57
+ generation_info=generation_info,
58
+ )
59
+ generations.append(gen)
60
+ token_usage = response.get("usage", {})
61
+ llm_output = {
62
+ "token_usage": token_usage,
63
+ "model_name": response["model"],
64
+ "system_fingerprint": response.get("system_fingerprint", ""),
65
+ }
66
+ return ChatResult(generations=generations, llm_output=llm_output)
67
+
68
+ def bind_tools(
69
+ self,
70
+ tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
71
+ *,
72
+ tool_choice: Optional[
73
+ Union[dict, str, Literal["auto", "any", "none"], bool]
74
+ ] = None,
75
+ **kwargs: Any,
76
+ ) -> Runnable[LanguageModelInput, BaseMessage]:
77
+ """Bind tool-like objects to this chat model.
78
+
79
+ Args:
80
+ tools: A list of tool definitions to bind to this chat model.
81
+ Can be a dictionary, pydantic model, callable, or BaseTool. Pydantic
82
+ models, callables, and BaseTools will be automatically converted to
83
+ their schema dictionary representation.
84
+ tool_choice: Which tool to require the model to call.
85
+ Must be the name of the single provided function,
86
+ "auto" to automatically determine which function to call
87
+ with the option to not call any function, "any" to enforce that some
88
+ function is called, or a dict of the form:
89
+ {"type": "function", "function": {"name": <<tool_name>>}}.
90
+ **kwargs: Any additional parameters to pass to the
91
+ :class:`~langchain.runnable.Runnable` constructor.
92
+ """
93
+ formatted_tools = [convert_to_openai_tool(tool) for tool in tools]
94
+ if tool_choice is not None and tool_choice:
95
+ if isinstance(tool_choice, str) and (
96
+ tool_choice not in ("auto", "any", "none")
97
+ ):
98
+ tool_choice = {"type": "function", "function": {"name": tool_choice}}
99
+ if isinstance(tool_choice, dict) and (len(formatted_tools) != 1):
100
+ raise ValueError(
101
+ "When specifying `tool_choice`, you must provide exactly one "
102
+ f"tool. Received {len(formatted_tools)} tools."
103
+ )
104
+ if isinstance(tool_choice, dict) and (
105
+ formatted_tools[0]["function"]["name"]
106
+ != tool_choice["function"]["name"]
107
+ ):
108
+ raise ValueError(
109
+ f"Tool choice {tool_choice} was specified, but the only "
110
+ f"provided tool was {formatted_tools[0]['function']['name']}."
111
+ )
112
+ if isinstance(tool_choice, bool):
113
+ if len(tools) > 1:
114
+ raise ValueError(
115
+ "tool_choice can only be True when there is one tool. Received "
116
+ f"{len(tools)} tools."
117
+ )
118
+ tool_name = formatted_tools[0]["function"]["name"]
119
+ tool_choice = {
120
+ "type": "function",
121
+ "function": {"name": tool_name},
122
+ }
123
+
124
+ kwargs["tool_choice"] = tool_choice
125
+ return super().bind(tools=formatted_tools, **kwargs)
126
+
127
+ def _generate(self, messages: List[BaseMessage], **kwargs) -> ChatResult:
128
+ messages_dicts = self._create_message_dicts(messages, [])
129
+ response = self.llm.chat(
130
+ messages_dicts,
131
+ model=kwargs.get("model", self.model),
132
+ is_stream=kwargs.get("is_stream", self.is_stream),
133
+ retries=kwargs.get("retries", self.retries),
134
+ parameters=kwargs.get("parameters", self.parameters),
135
+ **kwargs,
136
+ )
137
+ return self._create_chat_result(response)
@@ -0,0 +1,31 @@
1
+ try:
2
+ from llmstudio_tracker.tracker import Tracker, TrackingConfig
3
+ except ImportError:
4
+ name = None
5
+ message = f"{name} requires the 'llmstudio-tracker' package. Add it to the dependencies, or install it using 'pip install llmstudio[tracker]'."
6
+
7
+ class Tracker:
8
+ def __init__(self, *args, **kwargs):
9
+ raise ImportError(message.format(name="Tracker"))
10
+
11
+ class TrackingConfig:
12
+ def __init__(self, *args, **kwargs):
13
+ raise ImportError(message.format(name="TrackingConfig"))
14
+
15
+
16
+ try:
17
+ from llmstudio_proxy.provider import LLMProxyProvider, ProxyConfig
18
+ except ImportError:
19
+ name = None
20
+ message = f"{name} requires the 'llmstudio-proxy' package. Add it to the dependencies, or install it using 'pip install llmstudio[proxy]'."
21
+
22
+ class LLMProxyProvider:
23
+ def __init__(self, *args, **kwargs):
24
+ raise ImportError(message.format(name="LLMProxyProvider"))
25
+
26
+ class ProxyConfig:
27
+ def __init__(self, *args, **kwargs):
28
+ raise ImportError(message.format(name="ProxyConfig"))
29
+
30
+
31
+ from llmstudio.providers.provider import LLM
@@ -0,0 +1,143 @@
1
+ from typing import Any, Coroutine, Dict, Optional
2
+
3
+ from llmstudio.providers import LLMProxyProvider, ProxyConfig, Tracker, TrackingConfig
4
+ from llmstudio.utils import create_session_id
5
+ from llmstudio_core.providers import LLMCore
6
+ from llmstudio_core.providers.provider import Provider
7
+ from openai.types.chat import ChatCompletion, ChatCompletionChunk
8
+
9
+
10
+ class LLM(Provider):
11
+ def __init__(
12
+ self,
13
+ provider: str,
14
+ proxy_config: Optional[ProxyConfig] = None,
15
+ tracking_config: Optional[TrackingConfig] = None,
16
+ session_id: Optional[str] = None,
17
+ api_key: Optional[str] = None,
18
+ api_endpoint: Optional[str] = None,
19
+ api_version: Optional[str] = None,
20
+ base_url: Optional[str] = None,
21
+ **kwargs,
22
+ ):
23
+ """
24
+ Initializes an LLM provider instance to route your calls to the configured provider client.
25
+
26
+ This constructor sets up the LLM provider with optional proxy and tracking configurations.
27
+ If a proxy configuration is provided, it initializes an LLMProxyProvider; otherwise, it uses
28
+ LLMCore. It also sets up a tracker if a tracking configuration is provided. The session_id
29
+ is used to uniquely identify interactions within a session, and it requires a tracking
30
+ configuration to be specified.
31
+
32
+ Parameters:
33
+ - provider (str): The name of the LLM provider (e.g., "openai", "vertexai").
34
+ - proxy_config (Optional[ProxyConfig]): Configuration for proxy settings, applicable if proxy server is running.
35
+ - tracking_config (Optional[TrackingConfig]): Configuration for tracking interactions with the LLM. Applicable if Tracking server is running.
36
+ - session_id (Optional[str]): A unique identifier for the session, used for tracking purposes.
37
+
38
+ # If running without a Proxy Server:
39
+ # All providers:
40
+ - api_key (Optional[str]): The API key for authenticating requests to the LLM provider.
41
+
42
+ # Azure
43
+ - api_endpoint (Optional[str]): The specific API endpoint to use for requests.
44
+ - api_version (Optional[str]): The version of the API to use.
45
+ - base_url (Optional[str]): The base URL for the API requests.
46
+
47
+ - **kwargs: Additional keyword arguments for provider customization.
48
+
49
+ Raises:
50
+ - ValueError: If a session_id is provided without a tracking_config.
51
+ """
52
+ if proxy_config is not None:
53
+ self._provider = LLMProxyProvider(
54
+ provider=provider, proxy_config=proxy_config
55
+ )
56
+ else:
57
+ self._provider = LLMCore(
58
+ provider=provider,
59
+ api_key=api_key,
60
+ api_endpoint=api_endpoint,
61
+ api_version=api_version,
62
+ base_url=base_url,
63
+ **kwargs,
64
+ )
65
+
66
+ self._session_id = None
67
+ self._tracker = None
68
+ if tracking_config is not None:
69
+ self._tracker = Tracker(tracking_config=tracking_config)
70
+ self._session_id = create_session_id() if session_id is None else session_id
71
+
72
+ if (session_id is not None) and tracking_config is None:
73
+ raise ValueError(
74
+ f"'session_id' requires the 'tracking_config' specified and 'llmstudio[tracker]' installation."
75
+ )
76
+
77
+ def _provider_config_name(self):
78
+ return self._provider._provider_config_name()
79
+
80
+ def chat(
81
+ self,
82
+ chat_input: Any,
83
+ model: str,
84
+ is_stream: bool | None = False,
85
+ retries: int | None = 0,
86
+ parameters: Optional[dict] = {},
87
+ **kwargs,
88
+ ) -> ChatCompletionChunk | ChatCompletion:
89
+ result = self._provider.chat(
90
+ chat_input, model, is_stream, retries, parameters, **kwargs
91
+ )
92
+
93
+ if isinstance(result, (ChatCompletionChunk, ChatCompletion)):
94
+ if self._tracker:
95
+ result_dict = self._add_session_id(result, self._session_id)
96
+ self._tracker.log(result_dict)
97
+ return result
98
+ else:
99
+
100
+ def generator_wrapper():
101
+ for item in result:
102
+ yield item
103
+ if self._tracker and item.metrics:
104
+ result_dict = self._add_session_id(item, self._session_id)
105
+ self._tracker.log(result_dict)
106
+
107
+ return generator_wrapper()
108
+
109
+ async def achat(
110
+ self,
111
+ chat_input: Any,
112
+ model: str,
113
+ is_stream: bool | None = False,
114
+ retries: int | None = 0,
115
+ parameters: Optional[dict] = {},
116
+ **kwargs,
117
+ ) -> Coroutine[Any, Any, Coroutine[Any, Any, ChatCompletionChunk | ChatCompletion]]:
118
+ result = await self._provider.achat(
119
+ chat_input, model, is_stream, retries, parameters, **kwargs
120
+ )
121
+ if isinstance(result, (ChatCompletionChunk, ChatCompletion)):
122
+ if self._tracker:
123
+ result_dict = self._add_session_id(result, self._session_id)
124
+ self._tracker.log(result_dict)
125
+ return result
126
+ else:
127
+
128
+ async def async_generator_wrapper():
129
+ async for item in result:
130
+ yield item
131
+ if self._tracker and item.metrics:
132
+ result_dict = self._add_session_id(item, self._session_id)
133
+ self._tracker.log(result_dict)
134
+
135
+ return async_generator_wrapper()
136
+
137
+ @staticmethod
138
+ def _add_session_id(
139
+ result: ChatCompletionChunk | ChatCompletion, session_id: str
140
+ ) -> Dict[str, Any]:
141
+ result_dict = result.model_dump()
142
+ result_dict["session_id"] = session_id
143
+ return result_dict
@@ -0,0 +1,14 @@
1
+ _proxy_server_started = False
2
+ _tracker_server_started = False
3
+
4
+
5
+ def start_servers(proxy=True, tracker=True):
6
+ global _proxy_server_started, _tracker_server_started
7
+ if proxy and not _proxy_server_started:
8
+ from llmstudio_proxy.server import setup_engine_server
9
+
10
+ setup_engine_server()
11
+ if tracker and not _tracker_server_started:
12
+ from llmstudio_tracker.server import setup_tracking_server
13
+
14
+ setup_tracking_server()
@@ -0,0 +1,11 @@
1
+ import datetime
2
+ from uuid import uuid4
3
+
4
+
5
+ def create_session_id() -> str:
6
+ hash = str(uuid4())
7
+ try:
8
+ timestamp = datetime.datetime.now(datetime.UTC).strftime("%Y%m%d-%H%M%S")
9
+ except AttributeError: # python < 3.12
10
+ timestamp = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S")
11
+ return f"{timestamp}-{hash}"
@@ -0,0 +1,48 @@
1
+ [tool.poetry]
2
+ name = "llmstudio"
3
+ version = "0.0.1b1"
4
+ description = "Prompt Perfection at Your Fingertips"
5
+ authors = ["Cláudio Lemos <claudio@tensorops.ai>"]
6
+ license = "MIT"
7
+ homepage = "https://llmstudio.ai/"
8
+ repository = "https://github.com/tensoropsai/llmstudio"
9
+ documentation = "https://docs.llmstudio.ai"
10
+ readme = "README.md"
11
+ keywords = ["ml", "ai", "llm", "llmops", "openai", "langchain", "chatgpt", "llmstudio", "tensorops"]
12
+
13
+ [tool.poetry.urls]
14
+ "Bug Tracker" = "https://github.com/tensoropsai/llmstudio/issues"
15
+
16
+ [tool.poetry.dependencies]
17
+ python = "^3.9"
18
+ pydantic = "^2.5"
19
+ python-dotenv = "^0"
20
+ langchain = "^0"
21
+ langchain-experimental = "^0"
22
+ llmstudio-core = "0.0.1b1"
23
+ llmstudio-tracker = { version = "0.0.1b1", optional = true }
24
+ llmstudio-proxy = { version = "0.0.1b1", optional = true }
25
+
26
+ [tool.poetry.extras]
27
+ proxy = ["llmstudio-proxy"]
28
+ tracker = ["llmstudio-tracker"]
29
+
30
+ [tool.poetry.group.dev.dependencies]
31
+ pytest = "^7.4"
32
+ pytest-asyncio = "^0"
33
+ mypy = "^1.5"
34
+ ruff = "^0.0.284"
35
+ black = "^23.7"
36
+ pre-commit = "^3.7"
37
+ flake8= "^7"
38
+ ipykernel = "^6"
39
+ llmstudio-core = { path = "../core/", develop = true }
40
+ llmstudio-tracker = { path = "../tracker/", develop = true }
41
+ llmstudio-proxy = { path = "../proxy/", develop = true }
42
+
43
+ [tool.poetry.scripts]
44
+ llmstudio = "llmstudio.cli:main"
45
+
46
+ [build-system]
47
+ requires = ["poetry-core>=1.0.0"]
48
+ build-backend = "poetry.core.masonry.api"