llmstudio 0.2.0__tar.gz → 0.2.2__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.
- {llmstudio-0.2.0 → llmstudio-0.2.2}/PKG-INFO +3 -2
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio/__init__.py +1 -3
- llmstudio-0.2.2/llmstudio/engine/__init__.py +199 -0
- llmstudio-0.2.2/llmstudio/engine/config.py +405 -0
- llmstudio-0.2.2/llmstudio/engine/constants.py +28 -0
- llmstudio-0.2.2/llmstudio/engine/utils.py +59 -0
- llmstudio-0.2.2/llmstudio/models/__init__.py +4 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio/models/bedrock.py +3 -3
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio/models/openai.py +3 -2
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio/models/vertexai.py +3 -2
- llmstudio-0.2.2/llmstudio/ui/__init__.py +21 -0
- llmstudio-0.2.2/llmstudio/utils/rest_utils.py +53 -0
- llmstudio-0.2.2/llmstudio/validators/__init__.py +3 -0
- llmstudio-0.2.2/llmstudio/validators/bedrock.py +35 -0
- llmstudio-0.2.2/llmstudio/validators/openai.py +22 -0
- llmstudio-0.2.2/llmstudio/validators/vertexai.py +20 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio.egg-info/PKG-INFO +3 -2
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio.egg-info/SOURCES.txt +11 -1
- {llmstudio-0.2.0 → llmstudio-0.2.2}/setup.py +14 -2
- llmstudio-0.2.0/llmstudio/models/__init__.py +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/LICENSE +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/README.md +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio/cli.py +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio/client.py +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio/models/models.py +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio.egg-info/dependency_links.txt +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio.egg-info/entry_points.txt +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio.egg-info/requires.txt +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/llmstudio.egg-info/top_level.txt +0 -0
- {llmstudio-0.2.0 → llmstudio-0.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: llmstudio
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Prompt Perfection at Your Fingertips
|
|
5
5
|
Home-page: https://llmstudio.ai/
|
|
6
6
|
Author: TensorOps
|
|
@@ -8,5 +8,6 @@ Author-email: contact@tensorops.ai
|
|
|
8
8
|
Project-URL: Source Code, https://github.com/tensoropsai/llmstudio
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/tensoropsai/llmstudio/issues
|
|
10
10
|
Project-URL: Documentation, https://docs.llmstudio.ai
|
|
11
|
-
Keywords: ml ai llm llmstudio tensorops
|
|
11
|
+
Keywords: ml ai llm llmops openai langchain chatgpt llmstudio tensorops
|
|
12
|
+
Requires-Python: ~=3.9
|
|
12
13
|
License-File: LICENSE
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
name = "version"
|
|
2
|
-
__version__ = "0.2.
|
|
2
|
+
__version__ = "0.2.2"
|
|
3
3
|
|
|
4
4
|
__requirements__ = [
|
|
5
|
-
# core
|
|
6
5
|
"pydantic",
|
|
7
6
|
"requests",
|
|
8
7
|
"pydantic",
|
|
@@ -12,7 +11,6 @@ __requirements__ = [
|
|
|
12
11
|
"fastapi",
|
|
13
12
|
"uvicorn",
|
|
14
13
|
"PyYaml",
|
|
15
|
-
# engine
|
|
16
14
|
"openai",
|
|
17
15
|
"tiktoken",
|
|
18
16
|
"google-auth",
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
from typing import Any, Callable, Dict, Optional
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI, HTTPException, Request
|
|
4
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
5
|
+
|
|
6
|
+
from llmstudio.engine.config import EngineRouteConfig, Route, RouteType
|
|
7
|
+
from llmstudio.engine.constants import ENGINE_HEALTH_ENDPOINT, ENGINE_ROUTE_BASE, VERSION
|
|
8
|
+
from llmstudio.engine.providers import get_provider
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EngineAPI(FastAPI):
|
|
12
|
+
"""
|
|
13
|
+
Extends FastAPI to provide an API engine with dynamic routes based on the given configuration.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
dynamic_routes (Dict[str, Route]): A dictionary mapping from route names to Route objects.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, config: EngineRouteConfig, *args: Any, **kwargs: Any):
|
|
20
|
+
"""
|
|
21
|
+
Initialize the EngineAPI instance.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
config (EngineRouteConfig): The configuration object containing routes and other settings.
|
|
25
|
+
*args (Any): Additional positional arguments.
|
|
26
|
+
**kwargs (Any): Additional keyword arguments.
|
|
27
|
+
"""
|
|
28
|
+
super().__init__(*args, **kwargs)
|
|
29
|
+
self.dynamic_routes: Dict[str, Route] = {}
|
|
30
|
+
self.set_dynamic_routes(config)
|
|
31
|
+
|
|
32
|
+
def set_dynamic_routes(self, config: EngineRouteConfig) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Clears existing dynamic routes and sets new ones based on the provided configuration.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
config (EngineRouteConfig): The configuration object containing routes and other settings.
|
|
38
|
+
"""
|
|
39
|
+
self.dynamic_routes.clear()
|
|
40
|
+
for route in config.routes:
|
|
41
|
+
for provider in route.model_providers:
|
|
42
|
+
self._add_dynamic_route(route, provider)
|
|
43
|
+
|
|
44
|
+
def _add_dynamic_route(self, route: dict, provider: dict):
|
|
45
|
+
"""
|
|
46
|
+
Internal method to add a dynamic route based on route and provider information.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
route (dict): Dictionary containing information about the route.
|
|
50
|
+
provider (dict): Dictionary containing information about the provider.
|
|
51
|
+
"""
|
|
52
|
+
provider_name = provider.provider
|
|
53
|
+
provider_config = provider.config or {}
|
|
54
|
+
route_type_name = f"{route.route_type.value}/{provider_name}"
|
|
55
|
+
path = f"{ENGINE_ROUTE_BASE}{route_type_name}"
|
|
56
|
+
|
|
57
|
+
self.add_api_route(
|
|
58
|
+
path=path,
|
|
59
|
+
endpoint=self._route_type_to_endpoint(
|
|
60
|
+
provider_name, provider_config, route.route_type
|
|
61
|
+
),
|
|
62
|
+
methods=["POST"],
|
|
63
|
+
)
|
|
64
|
+
self.dynamic_routes[route_type_name] = route.to_route(provider_name, path)
|
|
65
|
+
|
|
66
|
+
def _route_type_to_endpoint(
|
|
67
|
+
self, provider_name: str, provider_config: dict, route_type: RouteType
|
|
68
|
+
) -> Callable:
|
|
69
|
+
"""
|
|
70
|
+
Maps a route type to its corresponding endpoint callable based on provider information.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
provider_name (str): The name of the provider.
|
|
74
|
+
provider_config (dict): Configuration specific to the provider.
|
|
75
|
+
route_type (RouteType): The type of the route.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Callable: The callable endpoint to be used for the route.
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
HTTPException: If the route type is unexpected for the given provider.
|
|
82
|
+
"""
|
|
83
|
+
provider_to_factory = {
|
|
84
|
+
RouteType.LLM_CHAT: "chat",
|
|
85
|
+
RouteType.LLM_VALIDATION: "test",
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
factory = provider_to_factory.get(route_type)
|
|
89
|
+
if factory:
|
|
90
|
+
return self._create_generic_endpoint(factory, provider_name, provider_config)
|
|
91
|
+
|
|
92
|
+
raise HTTPException(
|
|
93
|
+
status_code=404,
|
|
94
|
+
detail=f"Unexpected route type {route_type!r} for provider {provider_name!r}.",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def _create_generic_endpoint(
|
|
98
|
+
self, method_name: str, provider_name: str, provider_config: str
|
|
99
|
+
) -> Callable:
|
|
100
|
+
"""
|
|
101
|
+
Creates a generic endpoint for the given method name and provider.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
method_name (str): The method name that should be invoked on the provider.
|
|
105
|
+
provider_name (str): The name of the provider.
|
|
106
|
+
provider_config (str): Configuration specific to the provider.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Callable: A generic endpoint that invokes the specified method on the provider.
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
HTTPException: If the specified method is not found for the given provider.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
async def _generic_endpoint(request: Request):
|
|
116
|
+
payload = await request.json()
|
|
117
|
+
api_key = payload.get("api_key", None)
|
|
118
|
+
provider_instance = get_provider(provider_name)(provider_config, api_key)
|
|
119
|
+
method = getattr(provider_instance, method_name, None)
|
|
120
|
+
|
|
121
|
+
if not method:
|
|
122
|
+
raise HTTPException(
|
|
123
|
+
status_code=404,
|
|
124
|
+
detail=f"Method {method_name!r} not found for provider {provider_name!r}.",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return await method(payload)
|
|
128
|
+
|
|
129
|
+
return _generic_endpoint
|
|
130
|
+
|
|
131
|
+
def get_dynamic_route(self, route_name: str) -> Optional[Route]:
|
|
132
|
+
"""
|
|
133
|
+
Retrieves the dynamic route by its name.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
route_name (str): The name of the route to retrieve.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Optional[Route]: The Route object if found, None otherwise.
|
|
140
|
+
"""
|
|
141
|
+
return self.dynamic_routes.get(route_name)
|
|
142
|
+
|
|
143
|
+
def get_dynamic_routes(self):
|
|
144
|
+
"""
|
|
145
|
+
Retrieves all dynamic routes.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Dict[str, Route]: A dictionary of all dynamic routes, keyed by route name.
|
|
149
|
+
"""
|
|
150
|
+
return self.dynamic_routes
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def create_app_from_config(config: EngineRouteConfig) -> EngineAPI:
|
|
154
|
+
"""
|
|
155
|
+
Initializes and returns an EngineAPI application based on the given configuration.
|
|
156
|
+
|
|
157
|
+
Parameters:
|
|
158
|
+
config (EngineRouteConfig): The configuration settings for initializing the EngineAPI.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
EngineAPI: An initialized EngineAPI application.
|
|
162
|
+
"""
|
|
163
|
+
app = EngineAPI(
|
|
164
|
+
config=config,
|
|
165
|
+
title="engine API",
|
|
166
|
+
description="The core API for engine",
|
|
167
|
+
version=VERSION,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
app.add_middleware(
|
|
171
|
+
CORSMiddleware,
|
|
172
|
+
allow_origins=["http://localhost:3000"],
|
|
173
|
+
allow_credentials=True,
|
|
174
|
+
allow_methods=["*"],
|
|
175
|
+
allow_headers=["*"],
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
@app.get(ENGINE_HEALTH_ENDPOINT)
|
|
179
|
+
async def health():
|
|
180
|
+
return {"status": "OK"}
|
|
181
|
+
|
|
182
|
+
@app.get(ENGINE_ROUTE_BASE + "{route_name}")
|
|
183
|
+
async def get_route(route_name: str) -> Route:
|
|
184
|
+
if matched := app.get_dynamic_route(route_name):
|
|
185
|
+
return matched
|
|
186
|
+
|
|
187
|
+
raise HTTPException(
|
|
188
|
+
status_code=404,
|
|
189
|
+
detail=f"The route '{route_name}' is not present or active on the server. Please "
|
|
190
|
+
"verify the route name.",
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
@app.get(ENGINE_ROUTE_BASE)
|
|
194
|
+
async def search_routes(page_token: Optional[str] = None):
|
|
195
|
+
# TODO: Implement better function
|
|
196
|
+
routes = app.get_dynamic_routes()
|
|
197
|
+
return routes
|
|
198
|
+
|
|
199
|
+
return app
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import pathlib
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import List, Optional, Union
|
|
7
|
+
|
|
8
|
+
import pydantic
|
|
9
|
+
import yaml
|
|
10
|
+
from packaging import version
|
|
11
|
+
from pydantic import BaseModel, ValidationError, validator
|
|
12
|
+
from pydantic.json import pydantic_encoder
|
|
13
|
+
|
|
14
|
+
from llmstudio.engine.utils import (
|
|
15
|
+
check_configuration_route_name_collisions,
|
|
16
|
+
is_valid_endpoint_name,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
IS_PYDANTIC_V2 = version.parse(pydantic.version.VERSION) >= version.parse("2.0")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class EngineConfig:
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
api_name="Engine",
|
|
26
|
+
host="localhost",
|
|
27
|
+
port=8000,
|
|
28
|
+
localhost=True,
|
|
29
|
+
config_path=os.path.join(os.path.dirname(__file__), "config.yaml"),
|
|
30
|
+
health_endpoint="health",
|
|
31
|
+
routes_endpoint="api/engine",
|
|
32
|
+
):
|
|
33
|
+
self.api_name = api_name
|
|
34
|
+
self.host = host
|
|
35
|
+
self.port = port
|
|
36
|
+
self.config_path = config_path
|
|
37
|
+
self.localhost = localhost
|
|
38
|
+
self.update_url()
|
|
39
|
+
self.update_endpoints(health_endpoint, routes_endpoint)
|
|
40
|
+
|
|
41
|
+
def update_url(self):
|
|
42
|
+
"""Update the URL based on the current host, port and localhost values."""
|
|
43
|
+
self.url = f"http://{self.host}:{self.port}" if self.localhost else self.host
|
|
44
|
+
|
|
45
|
+
def update_endpoints(self, health_endpoint, routes_endpoint):
|
|
46
|
+
"""Update the health and routes endpoints based on the current url."""
|
|
47
|
+
self.health_endpoint = f"{self.url}/{health_endpoint}"
|
|
48
|
+
self.routes_endpoint = f"{self.url}/{routes_endpoint}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RouteType(str, Enum):
|
|
52
|
+
"""
|
|
53
|
+
Used for specifying various types of routes in an API.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
LLM_CHAT = "chat"
|
|
57
|
+
LLM_VALIDATION = "validation"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Provider(str, Enum):
|
|
61
|
+
"""
|
|
62
|
+
Enum class to represent different AI service providers.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
OPENAI = "openai"
|
|
66
|
+
VERTEXAI = "vertexai"
|
|
67
|
+
BEDROCK = "bedrock"
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def values(cls):
|
|
71
|
+
return {p.value for p in cls}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class OpenAIConfig(BaseModel):
|
|
75
|
+
"""
|
|
76
|
+
OpenAIConfig is a class derived from BaseModel for handling the configuration needed for OpenAI API calls.
|
|
77
|
+
|
|
78
|
+
Attributes:
|
|
79
|
+
api_key (str): The API key for authentication.
|
|
80
|
+
openai_api_type (str, optional): Type of the OpenAI API to use, default is 'openai'.
|
|
81
|
+
openai_api_base (str, optional): Base URL for the OpenAI API, default is None.
|
|
82
|
+
openai_api_version (str, optional): API version, default is None.
|
|
83
|
+
openai_deployment_name (str, optional): Name of the deployment, default is None.
|
|
84
|
+
openai_organization (str, optional): Name of the organization, default is None.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
api_key: str
|
|
88
|
+
openai_api_type: Optional[str] = "openai"
|
|
89
|
+
openai_api_base: Optional[str] = None
|
|
90
|
+
openai_api_version: Optional[str] = None
|
|
91
|
+
openai_deployment_name: Optional[str] = None
|
|
92
|
+
openai_organization: Optional[str] = None
|
|
93
|
+
|
|
94
|
+
@validator("api_key", pre=True)
|
|
95
|
+
def validate_api_key(cls, value):
|
|
96
|
+
return _resolve_api_key_from_input(value)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class VertexAIConfig(BaseModel):
|
|
100
|
+
"""
|
|
101
|
+
VertexAIConfig is a class derived from BaseModel for handling the configuration needed for VertexAI API calls.
|
|
102
|
+
|
|
103
|
+
Attributes:
|
|
104
|
+
api_key (dict): The JSON API key for authentication.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
api_key: dict
|
|
108
|
+
|
|
109
|
+
@validator("api_key", pre=True)
|
|
110
|
+
def validate_api_key(cls, value):
|
|
111
|
+
return _resolve_api_key_from_input(value)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class BedrockConfig(BaseModel):
|
|
115
|
+
"""
|
|
116
|
+
BedrockConfig is a class derived from BaseModel for handling the configuration needed for Bedrock API calls.
|
|
117
|
+
|
|
118
|
+
Attributes:
|
|
119
|
+
api_key (dict): The JSON API key for authentication.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
api_key: dict
|
|
123
|
+
|
|
124
|
+
@validator("api_key", pre=True)
|
|
125
|
+
def validate_api_key(cls, value):
|
|
126
|
+
return _resolve_api_key_from_input(value)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
provider_configs = {
|
|
130
|
+
Provider.OPENAI: OpenAIConfig,
|
|
131
|
+
Provider.VERTEXAI: VertexAIConfig,
|
|
132
|
+
Provider.BEDROCK: BedrockConfig,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _resolve_api_key_from_input(api_key_input):
|
|
137
|
+
"""
|
|
138
|
+
Resolves the provided API key.
|
|
139
|
+
|
|
140
|
+
Input formats accepted:
|
|
141
|
+
|
|
142
|
+
- Path to a file as a string which will have the key loaded from it
|
|
143
|
+
- environment variable name that stores the api key
|
|
144
|
+
- the api key itself
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
if isinstance(api_key_input, dict):
|
|
148
|
+
api_key_input = api_key_input.get("api_key")
|
|
149
|
+
|
|
150
|
+
# try reading as an environment variable
|
|
151
|
+
if api_key_input.startswith("$"):
|
|
152
|
+
env_var_name = api_key_input[1:]
|
|
153
|
+
if env_var := os.getenv(env_var_name):
|
|
154
|
+
return env_var
|
|
155
|
+
else:
|
|
156
|
+
raise ValueError(f"Environment variable {env_var_name!r} is not set")
|
|
157
|
+
|
|
158
|
+
# try reading from a local path
|
|
159
|
+
file = pathlib.Path(api_key_input)
|
|
160
|
+
if file.is_file():
|
|
161
|
+
return file.read_text()
|
|
162
|
+
|
|
163
|
+
# if the key itself is passed, return
|
|
164
|
+
return api_key_input
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class ModelProvider(BaseModel):
|
|
168
|
+
"""
|
|
169
|
+
Represents a provider for machine learning models along with its configuration.
|
|
170
|
+
|
|
171
|
+
Attributes:
|
|
172
|
+
provider (Union[str, Provider]): The provider of the machine learning model.
|
|
173
|
+
config (Optional[Union[OpenAIConfig, ...]]): The configuration for the selected provider.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
provider: Union[str, Provider]
|
|
177
|
+
config: Optional[
|
|
178
|
+
Union[
|
|
179
|
+
OpenAIConfig,
|
|
180
|
+
VertexAIConfig,
|
|
181
|
+
BedrockConfig,
|
|
182
|
+
]
|
|
183
|
+
] = None
|
|
184
|
+
|
|
185
|
+
@validator("provider", pre=True)
|
|
186
|
+
def validate_provider(cls, value):
|
|
187
|
+
"""
|
|
188
|
+
Validates the 'provider' field.
|
|
189
|
+
|
|
190
|
+
Parameters:
|
|
191
|
+
value: The value to validate, can be either a string or an instance of the Provider enum.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Provider: A valid Provider enum instance.
|
|
195
|
+
|
|
196
|
+
Raises:
|
|
197
|
+
ValueError: If the provided value is not a valid provider.
|
|
198
|
+
"""
|
|
199
|
+
if isinstance(value, Provider):
|
|
200
|
+
return value
|
|
201
|
+
formatted_value = value.replace("-", "_").upper()
|
|
202
|
+
if formatted_value in Provider.__members__:
|
|
203
|
+
return Provider[formatted_value]
|
|
204
|
+
raise ValueError(f"The provider '{value}' is not supported.")
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def _validate_config(cls, info, values):
|
|
208
|
+
"""
|
|
209
|
+
Internal method to validate the 'config' field based on the provided 'provider'.
|
|
210
|
+
|
|
211
|
+
Parameters:
|
|
212
|
+
info: The configuration information to validate.
|
|
213
|
+
values: Dictionary containing other field values of the class, primarily used to fetch 'provider'.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
The validated configuration as an instance of the appropriate configuration class.
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
ValueError: If a valid 'provider' is not provided.
|
|
220
|
+
"""
|
|
221
|
+
if provider := values.get("provider"):
|
|
222
|
+
config_type = provider_configs[provider]
|
|
223
|
+
return config_type(**info)
|
|
224
|
+
|
|
225
|
+
raise ValueError("A provider must be provided for each gateway route.")
|
|
226
|
+
|
|
227
|
+
if IS_PYDANTIC_V2:
|
|
228
|
+
|
|
229
|
+
@validator("config", pre=True)
|
|
230
|
+
def validate_config(cls, info, values):
|
|
231
|
+
return cls._validate_config(info, values)
|
|
232
|
+
|
|
233
|
+
else:
|
|
234
|
+
|
|
235
|
+
@validator("config", pre=True)
|
|
236
|
+
def validate_config(cls, config, values):
|
|
237
|
+
return cls._validate_config(config, values)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class RouteConfig(BaseModel):
|
|
241
|
+
"""
|
|
242
|
+
Represents the configuration for a single route.
|
|
243
|
+
|
|
244
|
+
Attributes:
|
|
245
|
+
name (str): The name of the route.
|
|
246
|
+
route_type (RouteType): The type of the route, as defined in the RouteType enum.
|
|
247
|
+
model_providers (List[ModelProvider]): A list of model providers for the route.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
name: str
|
|
251
|
+
route_type: RouteType
|
|
252
|
+
model_providers: List[ModelProvider]
|
|
253
|
+
|
|
254
|
+
@validator("name", pre=True)
|
|
255
|
+
def validate_endpoint_name(cls, route_name):
|
|
256
|
+
"""
|
|
257
|
+
Validates that the provided route name is a valid endpoint name.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
route_name (str): The name of the route.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
str: The validated name.
|
|
264
|
+
|
|
265
|
+
Raises:
|
|
266
|
+
ValueError: If the name contains invalid characters.
|
|
267
|
+
"""
|
|
268
|
+
if not is_valid_endpoint_name(route_name):
|
|
269
|
+
raise ValueError(
|
|
270
|
+
"The route name provided contains disallowed characters for a url endpoint. "
|
|
271
|
+
f"'{route_name}' is invalid. Names cannot contain spaces or any non "
|
|
272
|
+
"alphanumeric characters other than hyphen and underscore."
|
|
273
|
+
)
|
|
274
|
+
return route_name
|
|
275
|
+
|
|
276
|
+
@validator("model_providers", pre=True)
|
|
277
|
+
def validate_model(cls, model_providers):
|
|
278
|
+
"""
|
|
279
|
+
Validates that the provided model providers list is not empty and contains valid entries.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
model_providers (List[Dict]): A list of dictionaries containing model provider information.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
List[Dict]: The validated list of model providers.
|
|
286
|
+
|
|
287
|
+
Raises:
|
|
288
|
+
ValueError: If the list is empty or contains invalid providers.
|
|
289
|
+
"""
|
|
290
|
+
if not model_providers:
|
|
291
|
+
raise ValueError(
|
|
292
|
+
"No model providers were provided for the route. Please provide at least one model provider."
|
|
293
|
+
)
|
|
294
|
+
for model_provider in model_providers:
|
|
295
|
+
if model_provider:
|
|
296
|
+
model_instance = ModelProvider(**model_provider)
|
|
297
|
+
if model_instance.provider not in Provider.values():
|
|
298
|
+
raise ValueError(
|
|
299
|
+
f"The provider entry for {model_instance.provider} is incorrect. Providers accepted are {Provider.values()}"
|
|
300
|
+
)
|
|
301
|
+
return model_providers
|
|
302
|
+
|
|
303
|
+
@validator("route_type", pre=True)
|
|
304
|
+
def validate_route_type(cls, value):
|
|
305
|
+
"""
|
|
306
|
+
Validates that the provided route type is a valid RouteType enum value.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
value (str): The type of the route.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
str: The validated route type.
|
|
313
|
+
|
|
314
|
+
Raises:
|
|
315
|
+
ValueError: If the route_type is not a valid RouteType enum value.
|
|
316
|
+
"""
|
|
317
|
+
if value in RouteType._value2member_map_:
|
|
318
|
+
return value
|
|
319
|
+
raise ValueError(
|
|
320
|
+
f"The route_type '{value}' is not supported. Please use one of {RouteType._value2member_map_}"
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
def to_route(self, name, route_url) -> "Route":
|
|
324
|
+
"""
|
|
325
|
+
Converts the configuration to a Route object.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
name (str): The name of the route.
|
|
329
|
+
route_url (str): The URL for the route.
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
Route: A Route object containing the route's configuration.
|
|
333
|
+
"""
|
|
334
|
+
return Route(
|
|
335
|
+
name=name,
|
|
336
|
+
route_type=self.route_type,
|
|
337
|
+
route_url=route_url,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class Route(BaseModel):
|
|
342
|
+
"""
|
|
343
|
+
A class to represent a routing information.
|
|
344
|
+
|
|
345
|
+
Attributes:
|
|
346
|
+
name (str): The name of the route.
|
|
347
|
+
route_type (str): The type of the route, usually represented as a string-based identifier.
|
|
348
|
+
route_url (str): The URL pattern for the route.
|
|
349
|
+
|
|
350
|
+
Config:
|
|
351
|
+
schema_extra: Provides an example instantiation of the Route class.
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
name: str
|
|
355
|
+
route_type: str
|
|
356
|
+
route_url: str
|
|
357
|
+
|
|
358
|
+
class Config:
|
|
359
|
+
schema_extra = {
|
|
360
|
+
"example": {
|
|
361
|
+
"name": "openai",
|
|
362
|
+
"route_type": "llm/v1/completions",
|
|
363
|
+
"route_url": "/engine/openai",
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
class EngineRouteConfig(BaseModel):
|
|
369
|
+
routes: List[RouteConfig]
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _load_route_config(path: Union[str, Path]) -> EngineRouteConfig:
|
|
373
|
+
"""
|
|
374
|
+
Reads the gateway configuration yaml file from the storage location and returns an instance
|
|
375
|
+
of the configuration RouteConfig class
|
|
376
|
+
"""
|
|
377
|
+
if isinstance(path, str):
|
|
378
|
+
path = Path(path)
|
|
379
|
+
try:
|
|
380
|
+
configuration = yaml.safe_load(path.read_text())
|
|
381
|
+
except Exception as e:
|
|
382
|
+
raise ValueError(f"The file at {path} is not a valid yaml file") from e
|
|
383
|
+
check_configuration_route_name_collisions(configuration)
|
|
384
|
+
try:
|
|
385
|
+
return EngineRouteConfig(**configuration)
|
|
386
|
+
except ValidationError as e:
|
|
387
|
+
raise ValueError(f"The gateway configuration is invalid: {e}") from e
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _save_route_config(config: EngineRouteConfig, path: Union[str, Path]) -> None:
|
|
391
|
+
if isinstance(path, str):
|
|
392
|
+
path = Path(path)
|
|
393
|
+
path.write_text(
|
|
394
|
+
yaml.safe_dump(json.loads(json.dumps(config.dict(), default=pydantic_encoder)))
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _validate_config(config_path: str) -> EngineRouteConfig:
|
|
399
|
+
if not os.path.exists(config_path):
|
|
400
|
+
raise ValueError(f"{config_path} does not exist")
|
|
401
|
+
|
|
402
|
+
try:
|
|
403
|
+
return _load_route_config(config_path)
|
|
404
|
+
except ValidationError as e:
|
|
405
|
+
raise ValueError(f"Invalid gateway configuration: {e}") from e
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
ENGINE_ROUTE_BASE = "/api/engine/"
|
|
2
|
+
ENGINE_HEALTH_ENDPOINT = "/health"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# Change to llmstudio version
|
|
6
|
+
VERSION = "0.1.0"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
END_TOKEN = "<END_TOKEN>"
|
|
10
|
+
|
|
11
|
+
VERTEXAI_TOKEN_PRICE = 0.0000005
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
OPENAI_PRICING_DICT = {
|
|
15
|
+
"gpt-3.5-turbo": {"input_tokens": 0.0000015, "output_tokens": 0.000002},
|
|
16
|
+
"gpt-4": {"input_tokens": 0.00003, "output_tokens": 0.00006},
|
|
17
|
+
"gpt-3.5-turbo-16k": {"input_tokens": 0.00003, "output_tokens": 0.00004},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
TITAN_MODELS = ["amazon.titan-tg1-large"]
|
|
22
|
+
CLAUDE_MODELS = [
|
|
23
|
+
"anthropic.claude-instant-v1",
|
|
24
|
+
"anthropic.claude-v1",
|
|
25
|
+
"anthropic.claude-v2",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
BEDROCK_MODELS = TITAN_MODELS + CLAUDE_MODELS
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def is_valid_endpoint_name(name: str) -> bool:
|
|
5
|
+
"""
|
|
6
|
+
Check whether a string contains any URL reserved characters, spaces, or characters other
|
|
7
|
+
than alphanumeric, underscore, hyphen, and dot.
|
|
8
|
+
|
|
9
|
+
Returns True if the string doesn't contain any of these characters.
|
|
10
|
+
"""
|
|
11
|
+
pattern = r"[^\w\.-]"
|
|
12
|
+
return re.search(pattern, name) is None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def validate_provider_config(config, api_key):
|
|
16
|
+
"""
|
|
17
|
+
Validate and/or initialize a provider configuration based on input parameters.
|
|
18
|
+
|
|
19
|
+
Parameters:
|
|
20
|
+
- config (dict or None): Configuration dictionary for the provider. Can be None.
|
|
21
|
+
- api_key (str or None): API key for the provider. Can be None.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
dict: The modified or validated configuration dictionary.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
- ValueError: If both `config` and `api_key` are None.
|
|
28
|
+
"""
|
|
29
|
+
if not (config or api_key):
|
|
30
|
+
raise ValueError(f"Config was not specified neither an api_key was provided.")
|
|
31
|
+
if config is None:
|
|
32
|
+
config = {}
|
|
33
|
+
if api_key is not None:
|
|
34
|
+
config.setdefault("api_key", api_key)
|
|
35
|
+
return config
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def check_configuration_route_name_collisions(config):
|
|
39
|
+
"""
|
|
40
|
+
Checks for duplicate route names in the given configuration.
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
config (dict): The configuration dictionary containing a list of routes.
|
|
44
|
+
Each route should be a dictionary with a 'name' key.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
None: Returns None if there are no duplicates.
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: If there are duplicate route names found in the configuration.
|
|
51
|
+
"""
|
|
52
|
+
if len(config["routes"]) < 2:
|
|
53
|
+
return
|
|
54
|
+
names = [route["name"] for route in config["routes"]]
|
|
55
|
+
if len(names) != len(set(names)):
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"Duplicate names found in route configurations. Please remove the duplicate route "
|
|
58
|
+
"name from the configuration to ensure that route endpoints are created properly."
|
|
59
|
+
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from llmstudoi.models import LLMClient, LLMModel
|
|
2
|
-
|
|
3
1
|
from llmstudio.engine.config import EngineConfig
|
|
4
|
-
|
|
2
|
+
|
|
3
|
+
from ..validators import ClaudeParameters, TitanParameters
|
|
4
|
+
from .models import LLMClient, LLMModel
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class BedrockClient(LLMClient):
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
3
|
from llmstudio.engine.config import EngineConfig
|
|
4
|
-
|
|
5
|
-
from
|
|
4
|
+
|
|
5
|
+
from ..validators import OpenAIParameters
|
|
6
|
+
from .models import LLMClient, LLMModel
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class OpenAIClient(LLMClient):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from llmstudio.engine.config import EngineConfig
|
|
2
|
-
|
|
3
|
-
from
|
|
2
|
+
|
|
3
|
+
from ..validators import VertexAIParameters
|
|
4
|
+
from .models import LLMClient, LLMModel
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class VertexAIClient(LLMClient):
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI
|
|
4
|
+
from fastapi.staticfiles import StaticFiles
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_ui_app():
|
|
8
|
+
app = FastAPI()
|
|
9
|
+
|
|
10
|
+
app.mount(
|
|
11
|
+
"/static",
|
|
12
|
+
StaticFiles(directory=os.path.join(os.path.dirname(__file__), "build", "static")),
|
|
13
|
+
name="static",
|
|
14
|
+
)
|
|
15
|
+
app.mount(
|
|
16
|
+
"/",
|
|
17
|
+
StaticFiles(directory=os.path.join(os.path.dirname(__file__), "build"), html=True),
|
|
18
|
+
name="app",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
return app
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from threading import Thread
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
import uvicorn
|
|
5
|
+
|
|
6
|
+
from llmstudio.engine import create_app_from_config
|
|
7
|
+
from llmstudio.engine.config import EngineConfig, _load_route_config
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_api_running(url, name) -> bool:
|
|
11
|
+
try:
|
|
12
|
+
response = requests.get(url, timeout=5)
|
|
13
|
+
if response.status_code == 200:
|
|
14
|
+
print(f"API {name} is already running")
|
|
15
|
+
return True
|
|
16
|
+
except requests.RequestException as e:
|
|
17
|
+
return False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def run_engine_app(engine_config=EngineConfig()):
|
|
21
|
+
config = _load_route_config(engine_config.config_path)
|
|
22
|
+
engine = create_app_from_config(config)
|
|
23
|
+
print(f"Running {engine_config.api_name} on {engine_config.host}:{engine_config.port}")
|
|
24
|
+
uvicorn.run(
|
|
25
|
+
engine,
|
|
26
|
+
host=engine_config.host,
|
|
27
|
+
port=engine_config.port,
|
|
28
|
+
log_level="critical",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def run_ui_app(ui_server_app):
|
|
33
|
+
uvicorn.run(
|
|
34
|
+
ui_server_app,
|
|
35
|
+
host="localhost",
|
|
36
|
+
port=3000,
|
|
37
|
+
log_level="critical",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def run_apis(engine_config=EngineConfig(), ui_server_app=None, serverless=False):
|
|
42
|
+
if engine_config.localhost and not is_api_running(
|
|
43
|
+
engine_config.health_endpoint, engine_config.api_name
|
|
44
|
+
):
|
|
45
|
+
thread = Thread(target=run_engine_app, args=(engine_config,))
|
|
46
|
+
thread.daemon = True
|
|
47
|
+
thread.start()
|
|
48
|
+
|
|
49
|
+
if ui_server_app:
|
|
50
|
+
thread = Thread(target=run_ui_app, args=(ui_server_app,))
|
|
51
|
+
thread.daemon = True
|
|
52
|
+
thread.start()
|
|
53
|
+
thread.join()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ClaudeParameters(BaseModel):
|
|
7
|
+
"""
|
|
8
|
+
Model for validating and storing parameters specific to Claude model.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
temperature (Optional[float]): Controls randomness in the model's output.
|
|
12
|
+
max_tokens (Optional[int]): The maximum number of tokens in the output.
|
|
13
|
+
top_p (Optional[float]): Influences the diversity of output by controlling token sampling.
|
|
14
|
+
top_k (Optional[float]): Sets the number of the most likely next tokens to filter for.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
temperature: Optional[float] = Field(1, ge=0, le=1)
|
|
18
|
+
max_tokens: Optional[int] = Field(300, ge=1, le=2048)
|
|
19
|
+
top_p: Optional[float] = Field(0.999, ge=0, le=1)
|
|
20
|
+
top_k: Optional[int] = Field(250, ge=1, le=500)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TitanParameters(BaseModel):
|
|
24
|
+
"""
|
|
25
|
+
Model for validating and storing parameters specific to Titan model.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
temperature (Optional[float]): Controls randomness in the model's output.
|
|
29
|
+
max_tokens (Optional[int]): The maximum number of tokens in the output.
|
|
30
|
+
top_p (Optional[float]): Influences the diversity of output by controlling token sampling.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
temperature: Optional[float] = Field(0, ge=0, le=1)
|
|
34
|
+
max_tokens: Optional[int] = Field(512, ge=1, le=4096)
|
|
35
|
+
top_p: Optional[float] = Field(0.9, ge=0.1, le=1)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class OpenAIParameters(BaseModel):
|
|
7
|
+
"""
|
|
8
|
+
A Pydantic model for encapsulating parameters used in OpenAI API requests.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
temperature (Optional[float]): Controls randomness in the model's output.
|
|
12
|
+
max_tokens (Optional[int]): The maximum number of tokens in the output.
|
|
13
|
+
top_p (Optional[float]): Influences the diversity of output by controlling token sampling.
|
|
14
|
+
frequency_penalty (Optional[float]): Modifies the likelihood of tokens appearing based on their frequency.
|
|
15
|
+
presence_penalty (Optional[float]): Adjusts the likelihood of new tokens appearing.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
temperature: Optional[float] = Field(default=1, ge=0, le=2)
|
|
19
|
+
max_tokens: Optional[int] = Field(default=256, ge=1, le=2048)
|
|
20
|
+
top_p: Optional[float] = Field(default=1, ge=0, le=1)
|
|
21
|
+
frequency_penalty: Optional[float] = Field(default=0, ge=0, le=1)
|
|
22
|
+
presence_penalty: Optional[float] = Field(default=0, ge=0, le=1)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class VertexAIParameters(BaseModel):
|
|
7
|
+
"""
|
|
8
|
+
A Pydantic model that encapsulates parameters used for VertexAI API requests.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
temperature (Optional[float]): Controls randomness in the model's output.
|
|
12
|
+
max_tokens (Optional[int]): The maximum number of tokens in the output.
|
|
13
|
+
top_p (Optional[float]): Influences the diversity of output by controlling token sampling.
|
|
14
|
+
top_k (Optional[float]): Sets the number of the most likely next tokens to filter for.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
temperature: Optional[float] = Field(1, ge=0, le=1)
|
|
18
|
+
max_tokens: Optional[int] = Field(256, ge=1, le=1024)
|
|
19
|
+
top_p: Optional[float] = Field(1, ge=0, le=1)
|
|
20
|
+
top_k: Optional[float] = Field(40, ge=1, le=40)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: llmstudio
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Prompt Perfection at Your Fingertips
|
|
5
5
|
Home-page: https://llmstudio.ai/
|
|
6
6
|
Author: TensorOps
|
|
@@ -8,5 +8,6 @@ Author-email: contact@tensorops.ai
|
|
|
8
8
|
Project-URL: Source Code, https://github.com/tensoropsai/llmstudio
|
|
9
9
|
Project-URL: Bug Tracker, https://github.com/tensoropsai/llmstudio/issues
|
|
10
10
|
Project-URL: Documentation, https://docs.llmstudio.ai
|
|
11
|
-
Keywords: ml ai llm llmstudio tensorops
|
|
11
|
+
Keywords: ml ai llm llmops openai langchain chatgpt llmstudio tensorops
|
|
12
|
+
Requires-Python: ~=3.9
|
|
12
13
|
License-File: LICENSE
|
|
@@ -10,8 +10,18 @@ llmstudio.egg-info/dependency_links.txt
|
|
|
10
10
|
llmstudio.egg-info/entry_points.txt
|
|
11
11
|
llmstudio.egg-info/requires.txt
|
|
12
12
|
llmstudio.egg-info/top_level.txt
|
|
13
|
+
llmstudio/engine/__init__.py
|
|
14
|
+
llmstudio/engine/config.py
|
|
15
|
+
llmstudio/engine/constants.py
|
|
16
|
+
llmstudio/engine/utils.py
|
|
13
17
|
llmstudio/models/__init__.py
|
|
14
18
|
llmstudio/models/bedrock.py
|
|
15
19
|
llmstudio/models/models.py
|
|
16
20
|
llmstudio/models/openai.py
|
|
17
|
-
llmstudio/models/vertexai.py
|
|
21
|
+
llmstudio/models/vertexai.py
|
|
22
|
+
llmstudio/ui/__init__.py
|
|
23
|
+
llmstudio/utils/rest_utils.py
|
|
24
|
+
llmstudio/validators/__init__.py
|
|
25
|
+
llmstudio/validators/bedrock.py
|
|
26
|
+
llmstudio/validators/openai.py
|
|
27
|
+
llmstudio/validators/vertexai.py
|
|
@@ -14,14 +14,26 @@ setup(
|
|
|
14
14
|
},
|
|
15
15
|
author_email="contact@tensorops.ai",
|
|
16
16
|
description="Prompt Perfection at Your Fingertips",
|
|
17
|
-
keywords="ml ai llm llmstudio tensorops",
|
|
17
|
+
keywords="ml ai llm llmops openai langchain chatgpt llmstudio tensorops",
|
|
18
18
|
version=SDK_VERSION,
|
|
19
|
-
packages=[
|
|
19
|
+
packages=[
|
|
20
|
+
"llmstudio",
|
|
21
|
+
"llmstudio.engine",
|
|
22
|
+
"llmstudio.models",
|
|
23
|
+
"llmstudio.ui",
|
|
24
|
+
"llmstudio.utils",
|
|
25
|
+
"llmstudio.validators",
|
|
26
|
+
],
|
|
20
27
|
package_dir={
|
|
21
28
|
"llmstudio": "llmstudio",
|
|
29
|
+
"llmstudio.engine": "llmstudio/engine",
|
|
22
30
|
"llmstudio.models": "llmstudio/models",
|
|
31
|
+
"llmstudio.ui": "llmstudio/ui",
|
|
32
|
+
"llmstudio.utils": "llmstudio/utils",
|
|
33
|
+
"llmstudio.validators": "llmstudio/validators",
|
|
23
34
|
},
|
|
24
35
|
install_requires=REQUIREMENTS,
|
|
25
36
|
include_package_data=True,
|
|
26
37
|
entry_points={"console_scripts": ["llmstudio = llmstudio.cli:main"]},
|
|
38
|
+
python_requires="~=3.9",
|
|
27
39
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|