seekrai 0.0.1__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.
Files changed (56) hide show
  1. seekrai/__init__.py +64 -0
  2. seekrai/abstract/__init__.py +1 -0
  3. seekrai/abstract/api_requestor.py +710 -0
  4. seekrai/cli/__init__.py +0 -0
  5. seekrai/cli/api/__init__.py +0 -0
  6. seekrai/cli/api/chat.py +245 -0
  7. seekrai/cli/api/completions.py +107 -0
  8. seekrai/cli/api/files.py +125 -0
  9. seekrai/cli/api/finetune.py +175 -0
  10. seekrai/cli/api/images.py +82 -0
  11. seekrai/cli/api/models.py +42 -0
  12. seekrai/cli/cli.py +77 -0
  13. seekrai/client.py +154 -0
  14. seekrai/constants.py +32 -0
  15. seekrai/error.py +188 -0
  16. seekrai/filemanager.py +393 -0
  17. seekrai/legacy/__init__.py +0 -0
  18. seekrai/legacy/base.py +27 -0
  19. seekrai/legacy/complete.py +91 -0
  20. seekrai/legacy/embeddings.py +25 -0
  21. seekrai/legacy/files.py +140 -0
  22. seekrai/legacy/finetune.py +173 -0
  23. seekrai/legacy/images.py +25 -0
  24. seekrai/legacy/models.py +44 -0
  25. seekrai/resources/__init__.py +25 -0
  26. seekrai/resources/chat/__init__.py +24 -0
  27. seekrai/resources/chat/completions.py +241 -0
  28. seekrai/resources/completions.py +205 -0
  29. seekrai/resources/embeddings.py +100 -0
  30. seekrai/resources/files.py +173 -0
  31. seekrai/resources/finetune.py +425 -0
  32. seekrai/resources/images.py +156 -0
  33. seekrai/resources/models.py +75 -0
  34. seekrai/seekrflow_response.py +50 -0
  35. seekrai/types/__init__.py +67 -0
  36. seekrai/types/abstract.py +26 -0
  37. seekrai/types/chat_completions.py +151 -0
  38. seekrai/types/common.py +64 -0
  39. seekrai/types/completions.py +86 -0
  40. seekrai/types/embeddings.py +35 -0
  41. seekrai/types/error.py +16 -0
  42. seekrai/types/files.py +88 -0
  43. seekrai/types/finetune.py +218 -0
  44. seekrai/types/images.py +42 -0
  45. seekrai/types/models.py +43 -0
  46. seekrai/utils/__init__.py +28 -0
  47. seekrai/utils/_log.py +61 -0
  48. seekrai/utils/api_helpers.py +84 -0
  49. seekrai/utils/files.py +204 -0
  50. seekrai/utils/tools.py +75 -0
  51. seekrai/version.py +6 -0
  52. seekrai-0.0.1.dist-info/LICENSE +201 -0
  53. seekrai-0.0.1.dist-info/METADATA +401 -0
  54. seekrai-0.0.1.dist-info/RECORD +56 -0
  55. seekrai-0.0.1.dist-info/WHEEL +4 -0
  56. seekrai-0.0.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,82 @@
1
+ import base64
2
+ import pathlib
3
+
4
+ import click
5
+ from PIL import Image
6
+
7
+ from seekrai import seekrflow
8
+ from seekrai.types import ImageResponse
9
+ from seekrai.types.images import ImageChoicesData
10
+
11
+
12
+ @click.group()
13
+ @click.pass_context
14
+ def images(ctx: click.Context) -> None:
15
+ """Images generations API commands"""
16
+ pass
17
+
18
+
19
+ @images.command()
20
+ @click.pass_context
21
+ @click.argument("prompt", type=str, required=True)
22
+ @click.option("--model", type=str, required=True, help="Model name")
23
+ @click.option("--steps", type=int, default=20, help="Number of steps to run generation")
24
+ @click.option("--seed", type=int, default=None, help="Random seed")
25
+ @click.option("--n", type=int, default=1, help="Number of images to generate")
26
+ @click.option("--height", type=int, default=1024, help="Image height")
27
+ @click.option("--width", type=int, default=1024, help="Image width")
28
+ @click.option("--negative-prompt", type=str, default=None, help="Negative prompt")
29
+ @click.option(
30
+ "--output",
31
+ type=click.Path(exists=True, file_okay=False, resolve_path=True),
32
+ required=False,
33
+ default=pathlib.Path("."),
34
+ help="Output directory",
35
+ )
36
+ @click.option("--prefix", type=str, required=False, default="image-")
37
+ @click.option("--no-show", is_flag=True, help="Do not open images in viewer")
38
+ def generate(
39
+ ctx: click.Context,
40
+ prompt: str,
41
+ model: str,
42
+ steps: int,
43
+ seed: int,
44
+ n: int,
45
+ height: int,
46
+ width: int,
47
+ negative_prompt: str,
48
+ output: pathlib.Path,
49
+ prefix: str,
50
+ no_show: bool,
51
+ ) -> None:
52
+ """Generate image"""
53
+
54
+ client: SeekrFlow = ctx.obj
55
+
56
+ response = client.images.generate(
57
+ prompt=prompt,
58
+ model=model,
59
+ steps=steps,
60
+ seed=seed,
61
+ n=n,
62
+ height=height,
63
+ width=width,
64
+ negative_prompt=negative_prompt,
65
+ )
66
+
67
+ assert isinstance(response, ImageResponse)
68
+ assert isinstance(response.data, list)
69
+
70
+ for i, choice in enumerate(response.data):
71
+ assert isinstance(choice, ImageChoicesData)
72
+
73
+ with open(f"{output}/{prefix}{choice.index}.png", "wb") as f:
74
+ f.write(base64.b64decode(choice.b64_json))
75
+
76
+ click.echo(
77
+ f"Image [{i + 1}/{len(response.data)}] saved to {output}/{prefix}{choice.index}.png"
78
+ )
79
+
80
+ if not no_show:
81
+ image = Image.open(f"{output}/{prefix}{choice.index}.png")
82
+ image.show()
@@ -0,0 +1,42 @@
1
+ from textwrap import wrap
2
+
3
+ import click
4
+ from tabulate import tabulate
5
+
6
+ from seekrai import seekrflow
7
+ from seekrai.types.models import ModelObject
8
+
9
+
10
+ @click.group()
11
+ @click.pass_context
12
+ def models(ctx: click.Context) -> None:
13
+ """Models API commands"""
14
+ pass
15
+
16
+
17
+ @models.command()
18
+ @click.pass_context
19
+ def list(ctx: click.Context) -> None:
20
+ """List models"""
21
+ client: SeekrFlow = ctx.obj
22
+
23
+ response = client.models.list()
24
+
25
+ display_list = []
26
+
27
+ model: ModelObject
28
+ for model in response:
29
+ display_list.append(
30
+ {
31
+ "ID": "\n".join(wrap(model.id or "", width=30)),
32
+ "Name": "\n".join(wrap(model.display_name or "", width=30)),
33
+ "Organization": model.organization,
34
+ "Type": model.type,
35
+ "Context Length": model.context_length,
36
+ "License": "\n".join(wrap(model.license or "", width=30)),
37
+ "Input per 1M token": model.pricing.input,
38
+ "Output per 1M token": model.pricing.output,
39
+ }
40
+ )
41
+
42
+ click.echo(tabulate(display_list, headers="keys", tablefmt="grid"))
seekrai/cli/cli.py ADDED
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import Any
5
+
6
+ import click
7
+
8
+ import seekrai
9
+ from seekrai.cli.api.chat import chat, interactive
10
+ from seekrai.cli.api.completions import completions
11
+ from seekrai.cli.api.files import files
12
+ from seekrai.cli.api.finetune import fine_tuning
13
+ from seekrai.cli.api.images import images
14
+ from seekrai.cli.api.models import models
15
+ from seekrai.constants import MAX_RETRIES, TIMEOUT_SECS
16
+
17
+
18
+ def print_version(ctx: click.Context, params: Any, value: Any) -> None:
19
+ if not value or ctx.resilient_parsing:
20
+ return
21
+ click.echo(f"Version {seekrai.version}")
22
+ ctx.exit()
23
+
24
+
25
+ @click.group()
26
+ @click.pass_context
27
+ @click.option(
28
+ "--api-key",
29
+ type=str,
30
+ help="API Key. Defaults to environment variable `SEEKRFLOW_API_KEY`",
31
+ default=os.getenv("SEEKRFLOW_API_KEY"),
32
+ )
33
+ @click.option(
34
+ "--base-url", type=str, help="API Base URL. Defaults to SeekrFlow AI endpoint."
35
+ )
36
+ @click.option(
37
+ "--timeout", type=int, help=f"Request timeout. Defaults to {TIMEOUT_SECS} seconds"
38
+ )
39
+ @click.option(
40
+ "--max-retries",
41
+ type=int,
42
+ help=f"Maximum number of HTTP retries. Defaults to {MAX_RETRIES}.",
43
+ )
44
+ @click.option(
45
+ "--version",
46
+ is_flag=True,
47
+ callback=print_version,
48
+ expose_value=False,
49
+ is_eager=True,
50
+ help="Print version",
51
+ )
52
+ @click.option("--debug", help="Debug mode", is_flag=True)
53
+ def main(
54
+ ctx: click.Context,
55
+ api_key: str | None,
56
+ base_url: str | None,
57
+ timeout: int | None,
58
+ max_retries: int | None,
59
+ debug: bool | None,
60
+ ) -> None:
61
+ """This is a sample CLI tool."""
62
+ seekrai.log = "debug" if debug else None
63
+ ctx.obj = seekrai.SeekrFlow(
64
+ api_key=api_key, base_url=base_url, timeout=timeout, max_retries=max_retries
65
+ )
66
+
67
+
68
+ main.add_command(chat)
69
+ main.add_command(interactive)
70
+ main.add_command(completions)
71
+ main.add_command(images)
72
+ main.add_command(files)
73
+ main.add_command(fine_tuning)
74
+ main.add_command(models)
75
+
76
+ if __name__ == "__main__":
77
+ main()
seekrai/client.py ADDED
@@ -0,0 +1,154 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import Dict
5
+
6
+ from seekrai import resources
7
+ from seekrai.constants import BASE_URL, MAX_RETRIES, TIMEOUT_SECS
8
+ from seekrai.error import AuthenticationError
9
+ from seekrai.types import SeekrFlowClient
10
+ from seekrai.utils import enforce_trailing_slash
11
+
12
+
13
+ class SeekrFlow:
14
+ completions: resources.Completions
15
+ chat: resources.Chat
16
+ embeddings: resources.Embeddings
17
+ files: resources.Files
18
+ images: resources.Images
19
+ models: resources.Models
20
+ fine_tuning: resources.FineTuning
21
+
22
+ # client options
23
+ client: SeekrFlowClient
24
+
25
+ def __init__(
26
+ self,
27
+ *,
28
+ api_key: str | None = None,
29
+ base_url: str | None = None,
30
+ timeout: float | None = None,
31
+ max_retries: int | None = None,
32
+ supplied_headers: Dict[str, str] | None = None,
33
+ ) -> None:
34
+ """Construct a new synchronous seekrai client instance.
35
+
36
+ This automatically infers the following arguments from their corresponding environment variables if they are not provided:
37
+ - `api_key` from `SEEKRFLOW_API_KEY`
38
+ - `base_url` from `SEEKRFLOW_BASE_URL`
39
+ """
40
+
41
+ # get api key
42
+ if not api_key:
43
+ api_key = os.environ.get("SEEKR_API_KEY")
44
+
45
+ if not api_key:
46
+ raise AuthenticationError(
47
+ "The api_key client option must be set either by passing api_key to the client or by setting the "
48
+ "SEEKR_API_KEY environment variable"
49
+ )
50
+
51
+ # get base url
52
+ if not base_url:
53
+ base_url = os.environ.get("SEEKRFLOW_BASE_URL")
54
+
55
+ if not base_url:
56
+ base_url = BASE_URL
57
+
58
+ if timeout is None:
59
+ timeout = TIMEOUT_SECS
60
+
61
+ if max_retries is None:
62
+ max_retries = MAX_RETRIES
63
+
64
+ # SeekrFlowClient object
65
+ self.client = SeekrFlowClient(
66
+ api_key=api_key,
67
+ base_url=enforce_trailing_slash(base_url),
68
+ timeout=timeout,
69
+ max_retries=max_retries,
70
+ supplied_headers=supplied_headers,
71
+ )
72
+
73
+ self.completions = resources.Completions(self.client)
74
+ self.chat = resources.Chat(self.client)
75
+ self.embeddings = resources.Embeddings(self.client)
76
+ self.files = resources.Files(self.client)
77
+ self.images = resources.Images(self.client)
78
+ self.models = resources.Models(self.client)
79
+ self.fine_tuning = resources.FineTuning(self.client)
80
+ # self.instruction_fine_tuning = resources.InstructionFineTuning(self.client) # todo
81
+
82
+
83
+ class AsyncSeekrFlow:
84
+ completions: resources.AsyncCompletions
85
+ chat: resources.AsyncChat
86
+ embeddings: resources.AsyncEmbeddings
87
+ files: resources.AsyncFiles
88
+ images: resources.AsyncImages
89
+ models: resources.AsyncModels
90
+ fine_tuning: resources.AsyncFineTuning
91
+
92
+ # client options
93
+ client: SeekrFlowClient
94
+
95
+ def __init__(
96
+ self,
97
+ *,
98
+ api_key: str | None = None,
99
+ base_url: str | None = None,
100
+ timeout: float | None = None,
101
+ max_retries: int | None = None,
102
+ supplied_headers: Dict[str, str] | None = None,
103
+ ) -> None:
104
+ """Construct a new async seekrai client instance.
105
+
106
+ This automatically infers the following arguments from their corresponding environment variables if they are not provided:
107
+ - `api_key` from `SEEKRFLOW_API_KEY`
108
+ - `base_url` from `SEEKRFLOW_BASE_URL`
109
+ """
110
+
111
+ # get api key
112
+ if not api_key:
113
+ api_key = os.environ.get("SEEKRFLOW_API_KEY")
114
+
115
+ if not api_key:
116
+ raise AuthenticationError(
117
+ "The api_key client option must be set either by passing api_key to the client or by setting the "
118
+ "SEEKRFLOW_API_KEY environment variable"
119
+ )
120
+
121
+ # get base url
122
+ if not base_url:
123
+ base_url = os.environ.get("SEEKRFLOW_BASE_URL")
124
+
125
+ if not base_url:
126
+ base_url = BASE_URL
127
+
128
+ if timeout is None:
129
+ timeout = TIMEOUT_SECS
130
+
131
+ if max_retries is None:
132
+ max_retries = MAX_RETRIES
133
+
134
+ # SeekrFlowClient object
135
+ self.client = SeekrFlowClient(
136
+ api_key=api_key,
137
+ base_url=enforce_trailing_slash(base_url),
138
+ timeout=timeout,
139
+ max_retries=max_retries,
140
+ supplied_headers=supplied_headers,
141
+ )
142
+
143
+ self.completions = resources.AsyncCompletions(self.client)
144
+ self.chat = resources.AsyncChat(self.client)
145
+ self.embeddings = resources.AsyncEmbeddings(self.client)
146
+ self.files = resources.AsyncFiles(self.client)
147
+ self.images = resources.AsyncImages(self.client)
148
+ self.models = resources.AsyncModels(self.client)
149
+ self.fine_tuning = resources.AsyncFineTuning(self.client)
150
+
151
+
152
+ Client = SeekrFlow
153
+
154
+ AsyncClient = AsyncSeekrFlow
seekrai/constants.py ADDED
@@ -0,0 +1,32 @@
1
+ # Session constants
2
+ TIMEOUT_SECS = 600
3
+ MAX_SESSION_LIFETIME_SECS = 180
4
+ MAX_CONNECTION_RETRIES = 2
5
+ MAX_RETRIES = 5
6
+ INITIAL_RETRY_DELAY = 0.5
7
+ MAX_RETRY_DELAY = 8.0
8
+
9
+ # API defaults
10
+ # BASE_URL = "http://seekrflow-llm-training-svc.llm.k8s.prd.int.seekr.com/v1"
11
+ BASE_URL = "http://localhost:8000/v1"
12
+
13
+ # Download defaults
14
+ DOWNLOAD_BLOCK_SIZE = 10 * 1024 * 1024 # 10 MB
15
+ DISABLE_TQDM = False
16
+
17
+ # Messages
18
+ MISSING_API_KEY_MESSAGE = """SEEKR_API_KEY not found.
19
+ Please set it as an environment variable or set it as seekrai.api_key
20
+ Find your SEEKR_API_KEY at https://seekr.com/xxx"""
21
+
22
+ # Minimum number of samples required for fine-tuning file
23
+ MIN_SAMPLES = 100
24
+
25
+ # the number of bytes in a gigabyte, used to convert bytes to GB for readable comparison
26
+ NUM_BYTES_IN_GB = 2**30
27
+
28
+ # maximum number of GB sized files we support finetuning for
29
+ MAX_FILE_SIZE_GB = 4.9
30
+
31
+ # expected columns for Parquet files
32
+ PARQUET_EXPECTED_COLUMNS = ["input_ids", "attention_mask", "labels"]
seekrai/error.py ADDED
@@ -0,0 +1,188 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import Any, Dict
5
+
6
+ from requests import RequestException
7
+
8
+ from seekrai.types.error import SeekrFlowErrorResponse
9
+
10
+
11
+ class SeekrFlowException(Exception):
12
+ def __init__(
13
+ self,
14
+ message: (
15
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
16
+ ) = None,
17
+ headers: str | Dict[Any, Any] | None = None,
18
+ request_id: str | None = None,
19
+ http_status: int | None = None,
20
+ ) -> None:
21
+ _message = (
22
+ json.dumps(message.model_dump())
23
+ if isinstance(message, SeekrFlowErrorResponse)
24
+ else message
25
+ )
26
+ self._message = f"Error code: {http_status} - {_message}"
27
+
28
+ super(SeekrFlowException, self).__init__(self._message)
29
+
30
+ self.http_status = http_status
31
+ self.headers = headers or {}
32
+ self.request_id = request_id
33
+
34
+ def __repr__(self) -> str:
35
+ repr_message = json.dumps(
36
+ {
37
+ "response": self._message,
38
+ "status": self.http_status,
39
+ "request_id": self.request_id,
40
+ "headers": self.headers,
41
+ }
42
+ )
43
+ return "%s(%r)" % (self.__class__.__name__, repr_message)
44
+
45
+
46
+ class AuthenticationError(SeekrFlowException):
47
+ def __init__(
48
+ self,
49
+ message: (
50
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
51
+ ) = None,
52
+ **kwargs: Any,
53
+ ) -> None:
54
+ super().__init__(message=message, **kwargs)
55
+
56
+
57
+ class ResponseError(SeekrFlowException):
58
+ def __init__(
59
+ self,
60
+ message: (
61
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
62
+ ) = None,
63
+ **kwargs: Any,
64
+ ) -> None:
65
+ super().__init__(message=message, **kwargs)
66
+
67
+
68
+ class JSONError(SeekrFlowException):
69
+ def __init__(
70
+ self,
71
+ message: (
72
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
73
+ ) = None,
74
+ **kwargs: Any,
75
+ ) -> None:
76
+ super().__init__(message=message, **kwargs)
77
+
78
+
79
+ class InstanceError(SeekrFlowException):
80
+ def __init__(self, model: str | None = "model", **kwargs: Any) -> None:
81
+ super().__init__(**kwargs)
82
+ self.message = f"""No running instances for {model}.
83
+ You can start an instance with one of the following methods:
84
+ 1. navigating to the SeekrFlow Playground at api.seekrflow.ai
85
+ 2. starting one in python using seekrflow.Models.start(model_name)
86
+ 3. `$ seekrflow models start <MODEL_NAME>` at the command line.
87
+ See `seekrflow.Models.list()` in python or `$ seekrflow models list` in command line
88
+ to get an updated list of valid model names.
89
+ """
90
+
91
+
92
+ class RateLimitError(SeekrFlowException):
93
+ def __init__(
94
+ self,
95
+ message: (
96
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
97
+ ) = None,
98
+ **kwargs: Any,
99
+ ) -> None:
100
+ super().__init__(message=message, **kwargs)
101
+
102
+
103
+ class FileTypeError(SeekrFlowException):
104
+ def __init__(
105
+ self,
106
+ message: (
107
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
108
+ ) = None,
109
+ **kwargs: Any,
110
+ ) -> None:
111
+ super().__init__(message=message, **kwargs)
112
+
113
+
114
+ class AttributeError(SeekrFlowException):
115
+ def __init__(
116
+ self,
117
+ message: (
118
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
119
+ ) = None,
120
+ **kwargs: Any,
121
+ ) -> None:
122
+ super().__init__(message=message, **kwargs)
123
+
124
+
125
+ class Timeout(SeekrFlowException):
126
+ def __init__(
127
+ self,
128
+ message: (
129
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
130
+ ) = None,
131
+ **kwargs: Any,
132
+ ) -> None:
133
+ super().__init__(message=message, **kwargs)
134
+
135
+
136
+ class APIConnectionError(SeekrFlowException):
137
+ def __init__(
138
+ self,
139
+ message: (
140
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
141
+ ) = None,
142
+ **kwargs: Any,
143
+ ) -> None:
144
+ super().__init__(message=message, **kwargs)
145
+
146
+
147
+ class InvalidRequestError(SeekrFlowException):
148
+ def __init__(
149
+ self,
150
+ message: (
151
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
152
+ ) = None,
153
+ **kwargs: Any,
154
+ ) -> None:
155
+ super().__init__(message=message, **kwargs)
156
+
157
+
158
+ class APIError(SeekrFlowException):
159
+ def __init__(
160
+ self,
161
+ message: (
162
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
163
+ ) = None,
164
+ **kwargs: Any,
165
+ ) -> None:
166
+ super().__init__(message=message, **kwargs)
167
+
168
+
169
+ class ServiceUnavailableError(SeekrFlowException):
170
+ def __init__(
171
+ self,
172
+ message: (
173
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
174
+ ) = None,
175
+ **kwargs: Any,
176
+ ) -> None:
177
+ super().__init__(message=message, **kwargs)
178
+
179
+
180
+ class DownloadError(SeekrFlowException):
181
+ def __init__(
182
+ self,
183
+ message: (
184
+ SeekrFlowErrorResponse | Exception | str | RequestException | None
185
+ ) = None,
186
+ **kwargs: Any,
187
+ ) -> None:
188
+ super().__init__(message=message, **kwargs)