pyconverters-openai_vision 0.5.2__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.
- pyconverters_openai_vision/__init__.py +2 -0
- pyconverters_openai_vision/openai_utils.py +117 -0
- pyconverters_openai_vision/openai_vision.py +230 -0
- pyconverters_openai_vision-0.5.2.dist-info/METADATA +81 -0
- pyconverters_openai_vision-0.5.2.dist-info/RECORD +7 -0
- pyconverters_openai_vision-0.5.2.dist-info/WHEEL +4 -0
- pyconverters_openai_vision-0.5.2.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from logging import Logger
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
from openai import OpenAI
|
|
6
|
+
from openai.lib.azure import AzureOpenAI
|
|
7
|
+
from pymultirole_plugins.util import comma_separated_to_list
|
|
8
|
+
from strenum import StrEnum
|
|
9
|
+
|
|
10
|
+
logger = Logger("pymultirole")
|
|
11
|
+
DEFAULT_CHAT_GPT_MODEL = "gpt-4o-mini"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Now use default retry with backoff of openai api
|
|
15
|
+
def openai_chat_completion(prefix, **kwargs):
|
|
16
|
+
client = set_openai(prefix)
|
|
17
|
+
response = client.chat.completions.create(**kwargs)
|
|
18
|
+
return response
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def openai_list_models(prefix, **kwargs):
|
|
22
|
+
def sort_by_created(x):
|
|
23
|
+
if 'created' in x:
|
|
24
|
+
return x['created']
|
|
25
|
+
elif 'created_at' in x:
|
|
26
|
+
return x['created_at']
|
|
27
|
+
elif 'deprecated' in x:
|
|
28
|
+
return x['deprecated'] or 9999999999
|
|
29
|
+
else:
|
|
30
|
+
return x.id
|
|
31
|
+
|
|
32
|
+
models = []
|
|
33
|
+
client = set_openai(prefix)
|
|
34
|
+
if prefix.startswith("DEEPINFRA"):
|
|
35
|
+
deepinfra_url = client.base_url
|
|
36
|
+
deepinfra_models = {}
|
|
37
|
+
public_models_list_url = f"{deepinfra_url.scheme}://{deepinfra_url.host}/models/list"
|
|
38
|
+
response = requests.get(public_models_list_url,
|
|
39
|
+
headers={'Accept': "application/json", 'Authorization': f"Bearer {client.api_key}"})
|
|
40
|
+
if response.ok:
|
|
41
|
+
resp = response.json()
|
|
42
|
+
mods = sorted(resp, key=sort_by_created, reverse=True)
|
|
43
|
+
mods = list(
|
|
44
|
+
{m['model_name'] for m in mods if m['type'] == 'text-generation'})
|
|
45
|
+
deepinfra_models.update({m: m for m in mods})
|
|
46
|
+
|
|
47
|
+
private_models_list_url = f"{deepinfra_url.scheme}://{deepinfra_url.host}/models/private/list"
|
|
48
|
+
response = requests.get(private_models_list_url,
|
|
49
|
+
headers={'Accept': "application/json", 'Authorization': f"Bearer {client.api_key}"})
|
|
50
|
+
if response.ok:
|
|
51
|
+
resp = response.json()
|
|
52
|
+
mods = sorted(resp, key=sort_by_created, reverse=True)
|
|
53
|
+
mods = list(
|
|
54
|
+
{m['model_name'] for m in mods if m['type'] == 'text-generation'})
|
|
55
|
+
deepinfra_models.update({m: m for m in mods})
|
|
56
|
+
|
|
57
|
+
deployed_models_list_url = f"{deepinfra_url.scheme}://{deepinfra_url.host}/deploy/list/"
|
|
58
|
+
response = requests.get(deployed_models_list_url,
|
|
59
|
+
headers={'Accept': "application/json", 'Authorization': f"Bearer {client.api_key}"})
|
|
60
|
+
if response.ok:
|
|
61
|
+
resp = response.json()
|
|
62
|
+
mods = sorted(resp, key=sort_by_created, reverse=True)
|
|
63
|
+
mods = list(
|
|
64
|
+
{m['model_name'] for m in mods if m['task'] == 'text-generation' and m['status'] == 'running'})
|
|
65
|
+
deepinfra_models.update({m: m for m in mods})
|
|
66
|
+
models = [m for m in deepinfra_models.keys() if 'vision' in m.lower()]
|
|
67
|
+
elif prefix.startswith("AZURE"):
|
|
68
|
+
models = comma_separated_to_list(os.getenv(prefix + "OPENAI_DEPLOYMENT_ID", None))
|
|
69
|
+
else:
|
|
70
|
+
response = client.models.list(**kwargs)
|
|
71
|
+
models = sorted(response.data, key=sort_by_created, reverse=True)
|
|
72
|
+
models = [m.id for m in models]
|
|
73
|
+
return models
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def set_openai(prefix):
|
|
77
|
+
if prefix.startswith("AZURE"):
|
|
78
|
+
client = AzureOpenAI(
|
|
79
|
+
# This is the default and can be omitted
|
|
80
|
+
api_key=os.getenv(prefix + "OPENAI_API_KEY"),
|
|
81
|
+
azure_endpoint=os.getenv(prefix + "OPENAI_API_BASE", None),
|
|
82
|
+
api_version=os.getenv(prefix + "OPENAI_API_VERSION", None),
|
|
83
|
+
# azure_deployment=os.getenv(prefix + "OPENAI_DEPLOYMENT_ID", None)
|
|
84
|
+
)
|
|
85
|
+
else:
|
|
86
|
+
client = OpenAI(
|
|
87
|
+
# This is the default and can be omitted
|
|
88
|
+
api_key=os.getenv(prefix + "OPENAI_API_KEY"),
|
|
89
|
+
base_url=os.getenv(prefix + "OPENAI_API_BASE", None)
|
|
90
|
+
)
|
|
91
|
+
return client
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def gpt_filter(m: str):
|
|
95
|
+
return m.startswith('gpt') and not m.startswith('gpt-3.5-turbo-instruct') and 'vision' not in m
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
NO_DEPLOYED_MODELS = 'no deployed models - check API key'
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def create_openai_model_enum(name, prefix="", key=lambda m: m):
|
|
102
|
+
chat_gpt_models = []
|
|
103
|
+
default_chat_gpt_model = None
|
|
104
|
+
try:
|
|
105
|
+
chat_gpt_models = [m for m in openai_list_models(prefix) if key(m)]
|
|
106
|
+
if chat_gpt_models:
|
|
107
|
+
default_chat_gpt_model = DEFAULT_CHAT_GPT_MODEL if DEFAULT_CHAT_GPT_MODEL in chat_gpt_models else \
|
|
108
|
+
chat_gpt_models[0]
|
|
109
|
+
except BaseException:
|
|
110
|
+
logger.warning("Can't list models from endpoint", exc_info=True)
|
|
111
|
+
|
|
112
|
+
if len(chat_gpt_models) == 0:
|
|
113
|
+
chat_gpt_models = [NO_DEPLOYED_MODELS]
|
|
114
|
+
models = [("".join([c if c.isalnum() else "_" for c in m]), m) for m in chat_gpt_models]
|
|
115
|
+
model_enum = StrEnum(name, dict(models))
|
|
116
|
+
default_chat_gpt_model = model_enum(default_chat_gpt_model) if default_chat_gpt_model is not None else None
|
|
117
|
+
return model_enum, default_chat_gpt_model
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import os
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from logging import Logger
|
|
5
|
+
from typing import List, cast, Type, Dict, Any
|
|
6
|
+
|
|
7
|
+
import filetype as filetype
|
|
8
|
+
from pydantic import Field, BaseModel
|
|
9
|
+
from pymultirole_plugins.v1.converter import ConverterParameters, ConverterBase
|
|
10
|
+
from pymultirole_plugins.v1.schema import Document
|
|
11
|
+
from starlette.datastructures import UploadFile
|
|
12
|
+
|
|
13
|
+
from .openai_utils import NO_DEPLOYED_MODELS, \
|
|
14
|
+
openai_chat_completion, create_openai_model_enum
|
|
15
|
+
|
|
16
|
+
logger = Logger("pymultirole")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OpenAIVisionBaseParameters(ConverterParameters):
|
|
20
|
+
model_str: str = Field(
|
|
21
|
+
None, extra="internal"
|
|
22
|
+
)
|
|
23
|
+
model: str = Field(
|
|
24
|
+
None, extra="internal"
|
|
25
|
+
)
|
|
26
|
+
prompt: str = Field(
|
|
27
|
+
"Describe the image with a lot of details",
|
|
28
|
+
description="""Contains the prompt as a string""",
|
|
29
|
+
extra="multiline",
|
|
30
|
+
)
|
|
31
|
+
max_tokens: int = Field(
|
|
32
|
+
256,
|
|
33
|
+
description="""The maximum number of tokens to generate in the completion.
|
|
34
|
+
The token count of your prompt plus max_tokens cannot exceed the model's context length.
|
|
35
|
+
Most models have a context length of 2048 tokens (except for the newest models, which support 4096).""",
|
|
36
|
+
)
|
|
37
|
+
system_prompt: str = Field(
|
|
38
|
+
None,
|
|
39
|
+
description="""Contains the system prompt""",
|
|
40
|
+
extra="multiline,advanced",
|
|
41
|
+
)
|
|
42
|
+
temperature: float = Field(
|
|
43
|
+
1.0,
|
|
44
|
+
description="""What sampling temperature to use, between 0 and 2.
|
|
45
|
+
Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
|
|
46
|
+
We generally recommend altering this or `top_p` but not both.""",
|
|
47
|
+
extra="advanced",
|
|
48
|
+
)
|
|
49
|
+
top_p: int = Field(
|
|
50
|
+
1,
|
|
51
|
+
description="""An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass.
|
|
52
|
+
So 0.1 means only the tokens comprising the top 10% probability mass are considered.
|
|
53
|
+
We generally recommend altering this or `temperature` but not both.""",
|
|
54
|
+
extra="advanced",
|
|
55
|
+
)
|
|
56
|
+
n: int = Field(
|
|
57
|
+
1,
|
|
58
|
+
description="""How many completions to generate for each prompt.
|
|
59
|
+
Note: Because this parameter generates many completions, it can quickly consume your token quota.
|
|
60
|
+
Use carefully and ensure that you have reasonable settings for `max_tokens`.""",
|
|
61
|
+
extra="advanced",
|
|
62
|
+
)
|
|
63
|
+
best_of: int = Field(
|
|
64
|
+
1,
|
|
65
|
+
description="""Generates best_of completions server-side and returns the "best" (the one with the highest log probability per token).
|
|
66
|
+
Results cannot be streamed.
|
|
67
|
+
When used with `n`, `best_of` controls the number of candidate completions and `n` specifies how many to return – `best_of` must be greater than `n`.
|
|
68
|
+
Use carefully and ensure that you have reasonable settings for `max_tokens`.""",
|
|
69
|
+
extra="advanced",
|
|
70
|
+
)
|
|
71
|
+
presence_penalty: float = Field(
|
|
72
|
+
0.0,
|
|
73
|
+
description="""Number between -2.0 and 2.0.
|
|
74
|
+
Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.""",
|
|
75
|
+
extra="advanced",
|
|
76
|
+
)
|
|
77
|
+
frequency_penalty: float = Field(
|
|
78
|
+
0.0,
|
|
79
|
+
description="""Number between -2.0 and 2.0.
|
|
80
|
+
Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.""",
|
|
81
|
+
extra="advanced",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class OpenAIVisionModel(str, Enum):
|
|
86
|
+
gpt_4o_mini = "gpt-4o-mini"
|
|
87
|
+
gpt_4o = "gpt-4o"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class OpenAIVisionParameters(OpenAIVisionBaseParameters):
|
|
91
|
+
model: OpenAIVisionModel = Field(
|
|
92
|
+
OpenAIVisionModel.gpt_4o_mini,
|
|
93
|
+
description="""The [OpenAI model](https://platform.openai.com/docs/models) used for speech to text transcription. Options currently available:</br>
|
|
94
|
+
<li>`whisper-1` - state-of-the-art open source large-v2 Whisper model.
|
|
95
|
+
""", extra="pipeline-naming-hint"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
DEEPINFRA_PREFIX = "DEEPINFRA_"
|
|
100
|
+
DEEPINFRA_VISION_MODEL_ENUM, DEEPINFRA_DEFAULT_VISION_MODEL = create_openai_model_enum('DeepInfraVisionModel',
|
|
101
|
+
prefix=DEEPINFRA_PREFIX)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class DeepInfraOpenAIVisionParameters(OpenAIVisionBaseParameters):
|
|
105
|
+
model: DEEPINFRA_VISION_MODEL_ENUM = Field(
|
|
106
|
+
None,
|
|
107
|
+
description="""The [DeepInfra 'OpenAI compatible' model](https://deepinfra.com/models?type=automatic-speech-recognition) used for speech to text transcription. It must be deployed on your [DeepInfra dashboard](https://deepinfra.com/dash).
|
|
108
|
+
""", extra="pipeline-naming-hint"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# AZURE_PREFIX = "AZURE_"
|
|
113
|
+
#
|
|
114
|
+
#
|
|
115
|
+
# class AzureOpenAIVisionParameters(OpenAIVisionBaseParameters):
|
|
116
|
+
# model: OpenAIVisionModel = Field(
|
|
117
|
+
# OpenAIVisionModel.whisper_1,
|
|
118
|
+
# description="""The [Azure OpenAI model](https://platform.openai.com/docs/models) used for speech to text transcription. Options currently available:</br>
|
|
119
|
+
# <li>`whisper-1` - state-of-the-art open source large-v2 Whisper model.
|
|
120
|
+
# """, extra="pipeline-naming-hint"
|
|
121
|
+
# )
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class OpenAIVisionConverterBase(ConverterBase):
|
|
125
|
+
__doc__ = """Generate text using [OpenAI Text Completion](https://platform.openai.com/docs/guides/completion) API
|
|
126
|
+
You input some text as a prompt, and the model will generate a text completion that attempts to match whatever context or pattern you gave it."""
|
|
127
|
+
PREFIX: str = ""
|
|
128
|
+
|
|
129
|
+
def compute_args(self, params: OpenAIVisionBaseParameters, source: UploadFile
|
|
130
|
+
) -> Dict[str, Any]:
|
|
131
|
+
data = source.file.read()
|
|
132
|
+
rv = base64.b64encode(data)
|
|
133
|
+
messages = [{"role": "system", "content": params.system_prompt}] if params.system_prompt is not None else []
|
|
134
|
+
messages.append({"role": "user",
|
|
135
|
+
"content": [
|
|
136
|
+
{
|
|
137
|
+
"type": "text",
|
|
138
|
+
"text": params.prompt
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"type": "image_url",
|
|
142
|
+
"image_url": {
|
|
143
|
+
"url": f"data:image/jpeg;base64,{rv.decode('utf-8')}"
|
|
144
|
+
}
|
|
145
|
+
}]})
|
|
146
|
+
kwargs = {
|
|
147
|
+
'model': params.model_str,
|
|
148
|
+
'messages': messages,
|
|
149
|
+
'max_tokens': params.max_tokens,
|
|
150
|
+
'temperature': params.temperature,
|
|
151
|
+
'top_p': params.top_p,
|
|
152
|
+
'n': params.n,
|
|
153
|
+
'frequency_penalty': params.frequency_penalty,
|
|
154
|
+
'presence_penalty': params.presence_penalty,
|
|
155
|
+
}
|
|
156
|
+
return kwargs
|
|
157
|
+
|
|
158
|
+
def compute_result(self, **kwargs):
|
|
159
|
+
response = openai_chat_completion(self.PREFIX, **kwargs)
|
|
160
|
+
contents = []
|
|
161
|
+
for choice in response.choices:
|
|
162
|
+
if choice.message.content:
|
|
163
|
+
contents.append(choice.message.content)
|
|
164
|
+
if contents:
|
|
165
|
+
result = "\n".join(contents)
|
|
166
|
+
return result
|
|
167
|
+
|
|
168
|
+
def convert(self, source: UploadFile, parameters: ConverterParameters) \
|
|
169
|
+
-> List[Document]:
|
|
170
|
+
|
|
171
|
+
params: OpenAIVisionBaseParameters = cast(
|
|
172
|
+
OpenAIVisionBaseParameters, parameters
|
|
173
|
+
)
|
|
174
|
+
OPENAI_MODEL = os.getenv(self.PREFIX + "OPENAI_MODEL", None)
|
|
175
|
+
if OPENAI_MODEL:
|
|
176
|
+
params.model_str = OPENAI_MODEL
|
|
177
|
+
doc = None
|
|
178
|
+
try:
|
|
179
|
+
kind = filetype.guess(source.file)
|
|
180
|
+
source.file.seek(0)
|
|
181
|
+
if kind.mime.startswith("image"):
|
|
182
|
+
result = None
|
|
183
|
+
kwargs = self.compute_args(params, source)
|
|
184
|
+
if kwargs['model'] != NO_DEPLOYED_MODELS:
|
|
185
|
+
result = self.compute_result(**kwargs)
|
|
186
|
+
if result:
|
|
187
|
+
doc = Document(identifier=source.filename, text=result)
|
|
188
|
+
doc.properties = {"fileName": source.filename}
|
|
189
|
+
except BaseException as err:
|
|
190
|
+
raise err
|
|
191
|
+
if doc is None:
|
|
192
|
+
raise TypeError(f"Conversion of audio file {source.filename} failed")
|
|
193
|
+
return [doc]
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def get_model(cls) -> Type[BaseModel]:
|
|
197
|
+
return OpenAIVisionBaseParameters
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class OpenAIVisionConverter(OpenAIVisionConverterBase):
|
|
201
|
+
__doc__ = """Convert audio using [OpenAI Audio](https://platform.openai.com/docs/guides/speech-to-text) API"""
|
|
202
|
+
|
|
203
|
+
def convert(self, source: UploadFile, parameters: ConverterParameters) \
|
|
204
|
+
-> List[Document]:
|
|
205
|
+
params: OpenAIVisionParameters = cast(
|
|
206
|
+
OpenAIVisionParameters, parameters
|
|
207
|
+
)
|
|
208
|
+
params.model_str = params.model.value
|
|
209
|
+
return super().convert(source, params)
|
|
210
|
+
|
|
211
|
+
@classmethod
|
|
212
|
+
def get_model(cls) -> Type[BaseModel]:
|
|
213
|
+
return OpenAIVisionParameters
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class DeepInfraOpenAIVisionConverter(OpenAIVisionConverterBase):
|
|
217
|
+
__doc__ = """Convert images using [DeepInfra Vision](https://deepinfra.com/docs/tutorials/whisper) API"""
|
|
218
|
+
PREFIX = DEEPINFRA_PREFIX
|
|
219
|
+
|
|
220
|
+
def convert(self, source: UploadFile, parameters: ConverterParameters) \
|
|
221
|
+
-> List[Document]:
|
|
222
|
+
params: DeepInfraOpenAIVisionParameters = cast(
|
|
223
|
+
DeepInfraOpenAIVisionParameters, parameters
|
|
224
|
+
)
|
|
225
|
+
params.model_str = params.model.value
|
|
226
|
+
return super().convert(source, params)
|
|
227
|
+
|
|
228
|
+
@classmethod
|
|
229
|
+
def get_model(cls) -> Type[BaseModel]:
|
|
230
|
+
return DeepInfraOpenAIVisionParameters
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyconverters-openai_vision
|
|
3
|
+
Version: 0.5.2
|
|
4
|
+
Summary: OpenAIVision converter
|
|
5
|
+
Home-page: https://kairntech.com/
|
|
6
|
+
Author: Olivier Terrier
|
|
7
|
+
Author-email: olivier.terrier@kairntech.com
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Classifier: Intended Audience :: Information Technology
|
|
11
|
+
Classifier: Intended Audience :: System Administrators
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Topic :: Internet
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Classifier: Development Status :: 4 - Beta
|
|
22
|
+
Classifier: Environment :: Web Environment
|
|
23
|
+
Classifier: Framework :: AsyncIO
|
|
24
|
+
Classifier: Intended Audience :: Developers
|
|
25
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
27
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
28
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
29
|
+
Requires-Dist: pymultirole-plugins>=0.5.0,<0.6.0
|
|
30
|
+
Requires-Dist: openai==1.9.0
|
|
31
|
+
Requires-Dist: Jinja2
|
|
32
|
+
Requires-Dist: tenacity
|
|
33
|
+
Requires-Dist: log-with-context
|
|
34
|
+
Requires-Dist: StrEnum
|
|
35
|
+
Requires-Dist: filetype==1.0.13
|
|
36
|
+
Requires-Dist: requests
|
|
37
|
+
Requires-Dist: flit ; extra == "dev"
|
|
38
|
+
Requires-Dist: pre-commit ; extra == "dev"
|
|
39
|
+
Requires-Dist: bump2version ; extra == "dev"
|
|
40
|
+
Requires-Dist: sphinx ; extra == "docs"
|
|
41
|
+
Requires-Dist: sphinx-rtd-theme ; extra == "docs"
|
|
42
|
+
Requires-Dist: m2r2 ; extra == "docs"
|
|
43
|
+
Requires-Dist: sphinxcontrib.apidoc ; extra == "docs"
|
|
44
|
+
Requires-Dist: jupyter_sphinx ; extra == "docs"
|
|
45
|
+
Requires-Dist: pytest>=7.0 ; extra == "test"
|
|
46
|
+
Requires-Dist: pytest-cov ; extra == "test"
|
|
47
|
+
Requires-Dist: pytest-flake8 ; extra == "test"
|
|
48
|
+
Requires-Dist: pytest-black ; extra == "test"
|
|
49
|
+
Requires-Dist: flake8==3.9.2 ; extra == "test"
|
|
50
|
+
Requires-Dist: tox ; extra == "test"
|
|
51
|
+
Requires-Dist: dirty-equals ; extra == "test"
|
|
52
|
+
Requires-Dist: werkzeug==2.0.0 ; extra == "test"
|
|
53
|
+
Requires-Dist: flask==2.1.3 ; extra == "test"
|
|
54
|
+
Provides-Extra: dev
|
|
55
|
+
Provides-Extra: docs
|
|
56
|
+
Provides-Extra: test
|
|
57
|
+
|
|
58
|
+
## Requirements
|
|
59
|
+
|
|
60
|
+
- Python 3.8+
|
|
61
|
+
- Flit to put Python packages and modules on PyPI
|
|
62
|
+
- Pydantic for the data parts.
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
```
|
|
66
|
+
pip install flit
|
|
67
|
+
pip install pymultirole-plugins
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Publish the Python Package to PyPI
|
|
71
|
+
- Increment the version of your package in the `__init__.py` file:
|
|
72
|
+
```
|
|
73
|
+
"""An amazing package!"""
|
|
74
|
+
|
|
75
|
+
__version__ = 'x.y.z'
|
|
76
|
+
```
|
|
77
|
+
- Publish
|
|
78
|
+
```
|
|
79
|
+
flit publish
|
|
80
|
+
```
|
|
81
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
pyconverters_openai_vision/__init__.py,sha256=59Qc-bsfVK9kVVX4vnt2PQF_YysgYAQ-T0KEABdUD64,51
|
|
2
|
+
pyconverters_openai_vision/openai_utils.py,sha256=YNOcbeh1sJKeRvkaMh6FnixrUcOfF4rJIGKG5fmnUDo,4806
|
|
3
|
+
pyconverters_openai_vision/openai_vision.py,sha256=MoEyjYCZ75jWiMonaQdR1erpFBpWgYEiqabp9D8102Y,9562
|
|
4
|
+
pyconverters_openai_vision-0.5.2.dist-info/entry_points.txt,sha256=BeU_sWrRTfIdBnkWCBALpYNOrYOYIlRjQdpR2XQhkSU,285
|
|
5
|
+
pyconverters_openai_vision-0.5.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
|
6
|
+
pyconverters_openai_vision-0.5.2.dist-info/METADATA,sha256=d2SvC6t_svBjtYRBo-JLShoD_9fVg5ODMvlI9pDULRo,2635
|
|
7
|
+
pyconverters_openai_vision-0.5.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[pyconverters.plugins]
|
|
2
|
+
azure_openai_vision=pyconverters_openai_vision.openai_vision:AzureOpenAIVisionConverter
|
|
3
|
+
deepinfra_openai_vision=pyconverters_openai_vision.openai_vision:DeepInfraOpenAIVisionConverter
|
|
4
|
+
openai_vision=pyconverters_openai_vision.openai_vision:OpenAIVisionConverter
|
|
5
|
+
|