llama-index-llms-openai 0.3.2__tar.gz → 0.3.4__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.
- {llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/PKG-INFO +3 -3
- {llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/llama_index/llms/openai/base.py +21 -20
- {llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/llama_index/llms/openai/utils.py +74 -19
- {llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/pyproject.toml +6 -3
- {llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/README.md +0 -0
- {llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/llama_index/llms/openai/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: llama-index-llms-openai
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: llama-index llms openai integration
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: llama-index
|
|
@@ -11,8 +11,8 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Requires-Dist: llama-index-core (>=0.12.
|
|
15
|
-
Requires-Dist: openai (>=1.
|
|
14
|
+
Requires-Dist: llama-index-core (>=0.12.4,<0.13.0)
|
|
15
|
+
Requires-Dist: openai (>=1.57.1,<2.0.0)
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
|
|
18
18
|
# LlamaIndex Llms Integration: Openai
|
{llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/llama_index/llms/openai/base.py
RENAMED
|
@@ -2,10 +2,10 @@ import functools
|
|
|
2
2
|
from typing import (
|
|
3
3
|
TYPE_CHECKING,
|
|
4
4
|
Any,
|
|
5
|
-
Generator,
|
|
6
5
|
Awaitable,
|
|
7
6
|
Callable,
|
|
8
7
|
Dict,
|
|
8
|
+
Generator,
|
|
9
9
|
List,
|
|
10
10
|
Optional,
|
|
11
11
|
Protocol,
|
|
@@ -18,6 +18,8 @@ from typing import (
|
|
|
18
18
|
|
|
19
19
|
import httpx
|
|
20
20
|
import tiktoken
|
|
21
|
+
|
|
22
|
+
import llama_index.core.instrumentation as instrument
|
|
21
23
|
from llama_index.core.base.llms.generic_utils import (
|
|
22
24
|
achat_to_completion_decorator,
|
|
23
25
|
acompletion_to_chat_decorator,
|
|
@@ -39,7 +41,11 @@ from llama_index.core.base.llms.types import (
|
|
|
39
41
|
LLMMetadata,
|
|
40
42
|
MessageRole,
|
|
41
43
|
)
|
|
42
|
-
from llama_index.core.bridge.pydantic import
|
|
44
|
+
from llama_index.core.bridge.pydantic import (
|
|
45
|
+
BaseModel,
|
|
46
|
+
Field,
|
|
47
|
+
PrivateAttr,
|
|
48
|
+
)
|
|
43
49
|
from llama_index.core.callbacks import CallbackManager
|
|
44
50
|
from llama_index.core.constants import (
|
|
45
51
|
DEFAULT_TEMPERATURE,
|
|
@@ -50,7 +56,8 @@ from llama_index.core.llms.callbacks import (
|
|
|
50
56
|
)
|
|
51
57
|
from llama_index.core.llms.function_calling import FunctionCallingLLM
|
|
52
58
|
from llama_index.core.llms.llm import ToolSelection
|
|
53
|
-
from llama_index.core.
|
|
59
|
+
from llama_index.core.llms.utils import parse_partial_json
|
|
60
|
+
from llama_index.core.types import BaseOutputParser, Model, PydanticProgramMode
|
|
54
61
|
from llama_index.llms.openai.utils import (
|
|
55
62
|
O1_MODELS,
|
|
56
63
|
OpenAIToolCall,
|
|
@@ -62,14 +69,10 @@ from llama_index.llms.openai.utils import (
|
|
|
62
69
|
is_function_calling_model,
|
|
63
70
|
openai_modelname_to_contextsize,
|
|
64
71
|
resolve_openai_credentials,
|
|
65
|
-
to_openai_message_dicts,
|
|
66
72
|
resolve_tool_choice,
|
|
73
|
+
to_openai_message_dicts,
|
|
67
74
|
update_tool_calls,
|
|
68
75
|
)
|
|
69
|
-
from llama_index.core.bridge.pydantic import (
|
|
70
|
-
BaseModel,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
76
|
from openai import AsyncOpenAI, AzureOpenAI
|
|
74
77
|
from openai import OpenAI as SyncOpenAI
|
|
75
78
|
from openai.types.chat.chat_completion_chunk import (
|
|
@@ -77,9 +80,6 @@ from openai.types.chat.chat_completion_chunk import (
|
|
|
77
80
|
ChoiceDelta,
|
|
78
81
|
ChoiceDeltaToolCall,
|
|
79
82
|
)
|
|
80
|
-
from llama_index.core.llms.utils import parse_partial_json
|
|
81
|
-
|
|
82
|
-
import llama_index.core.instrumentation as instrument
|
|
83
83
|
|
|
84
84
|
dispatcher = instrument.get_dispatcher(__name__)
|
|
85
85
|
|
|
@@ -89,7 +89,7 @@ if TYPE_CHECKING:
|
|
|
89
89
|
DEFAULT_OPENAI_MODEL = "gpt-3.5-turbo"
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
def llm_retry_decorator(f: Callable[
|
|
92
|
+
def llm_retry_decorator(f: Callable[..., Any]) -> Callable[..., Any]:
|
|
93
93
|
@functools.wraps(f)
|
|
94
94
|
def wrapper(self, *args: Any, **kwargs: Any) -> Any:
|
|
95
95
|
max_retries = getattr(self, "max_retries", 0)
|
|
@@ -112,7 +112,7 @@ def llm_retry_decorator(f: Callable[[Any], Any]) -> Callable[[Any], Any]:
|
|
|
112
112
|
class Tokenizer(Protocol):
|
|
113
113
|
"""Tokenizers support an encode function that returns a list of ints."""
|
|
114
114
|
|
|
115
|
-
def encode(self, text: str) -> List[int]:
|
|
115
|
+
def encode(self, text: str) -> List[int]: # fmt: skip
|
|
116
116
|
...
|
|
117
117
|
|
|
118
118
|
|
|
@@ -828,7 +828,7 @@ class OpenAI(FunctionCallingLLM):
|
|
|
828
828
|
|
|
829
829
|
def _prepare_chat_with_tools(
|
|
830
830
|
self,
|
|
831
|
-
tools:
|
|
831
|
+
tools: Sequence["BaseTool"],
|
|
832
832
|
user_msg: Optional[Union[str, ChatMessage]] = None,
|
|
833
833
|
chat_history: Optional[List[ChatMessage]] = None,
|
|
834
834
|
verbose: bool = False,
|
|
@@ -850,9 +850,8 @@ class OpenAI(FunctionCallingLLM):
|
|
|
850
850
|
for tool_spec in tool_specs:
|
|
851
851
|
if tool_spec["type"] == "function":
|
|
852
852
|
tool_spec["function"]["strict"] = strict
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
] = False # in current openai 1.40.0 it is always false.
|
|
853
|
+
# in current openai 1.40.0 it is always false.
|
|
854
|
+
tool_spec["function"]["parameters"]["additionalProperties"] = False
|
|
856
855
|
|
|
857
856
|
if isinstance(user_msg, str):
|
|
858
857
|
user_msg = ChatMessage(role=MessageRole.USER, content=user_msg)
|
|
@@ -871,7 +870,7 @@ class OpenAI(FunctionCallingLLM):
|
|
|
871
870
|
def _validate_chat_with_tools_response(
|
|
872
871
|
self,
|
|
873
872
|
response: ChatResponse,
|
|
874
|
-
tools:
|
|
873
|
+
tools: Sequence["BaseTool"],
|
|
875
874
|
allow_parallel_tool_calls: bool = False,
|
|
876
875
|
**kwargs: Any,
|
|
877
876
|
) -> ChatResponse:
|
|
@@ -966,7 +965,7 @@ class OpenAI(FunctionCallingLLM):
|
|
|
966
965
|
return super().stream_structured_predict(*args, llm_kwargs=llm_kwargs, **kwargs)
|
|
967
966
|
|
|
968
967
|
@dispatcher.span
|
|
969
|
-
def
|
|
968
|
+
async def astream_structured_predict(
|
|
970
969
|
self, *args: Any, llm_kwargs: Optional[Dict[str, Any]] = None, **kwargs: Any
|
|
971
970
|
) -> Generator[Union[Model, List[Model]], None, None]:
|
|
972
971
|
"""Stream structured predict."""
|
|
@@ -978,4 +977,6 @@ class OpenAI(FunctionCallingLLM):
|
|
|
978
977
|
)
|
|
979
978
|
# by default structured prediction uses function calling to extract structured outputs
|
|
980
979
|
# here we force tool_choice to be required
|
|
981
|
-
return super().
|
|
980
|
+
return await super().astream_structured_predict(
|
|
981
|
+
*args, llm_kwargs=llm_kwargs, **kwargs
|
|
982
|
+
)
|
{llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/llama_index/llms/openai/utils.py
RENAMED
|
@@ -3,9 +3,6 @@ import os
|
|
|
3
3
|
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type, Union
|
|
4
4
|
|
|
5
5
|
from deprecated import deprecated
|
|
6
|
-
from llama_index.core.base.llms.types import ChatMessage, LogProb, CompletionResponse
|
|
7
|
-
from llama_index.core.bridge.pydantic import BaseModel
|
|
8
|
-
from llama_index.core.base.llms.generic_utils import get_from_param_or_env
|
|
9
6
|
from tenacity import (
|
|
10
7
|
before_sleep_log,
|
|
11
8
|
retry,
|
|
@@ -18,12 +15,19 @@ from tenacity import (
|
|
|
18
15
|
from tenacity.stop import stop_base
|
|
19
16
|
|
|
20
17
|
import openai
|
|
18
|
+
from llama_index.core.base.llms.generic_utils import get_from_param_or_env
|
|
19
|
+
from llama_index.core.base.llms.types import (
|
|
20
|
+
ChatMessage,
|
|
21
|
+
ImageBlock,
|
|
22
|
+
LogProb,
|
|
23
|
+
TextBlock,
|
|
24
|
+
)
|
|
25
|
+
from llama_index.core.bridge.pydantic import BaseModel
|
|
21
26
|
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionMessageToolCall
|
|
22
27
|
from openai.types.chat.chat_completion_chunk import ChoiceDeltaToolCall
|
|
23
28
|
from openai.types.chat.chat_completion_message import ChatCompletionMessage
|
|
24
29
|
from openai.types.chat.chat_completion_token_logprob import ChatCompletionTokenLogprob
|
|
25
30
|
from openai.types.completion_choice import Logprobs
|
|
26
|
-
from openai.types.completion import Completion
|
|
27
31
|
|
|
28
32
|
DEFAULT_OPENAI_API_TYPE = "open_ai"
|
|
29
33
|
DEFAULT_OPENAI_API_BASE = "https://api.openai.com/v1"
|
|
@@ -190,7 +194,8 @@ def create_retry_decorator(
|
|
|
190
194
|
|
|
191
195
|
|
|
192
196
|
def openai_modelname_to_contextsize(modelname: str) -> int:
|
|
193
|
-
"""
|
|
197
|
+
"""
|
|
198
|
+
Calculate the maximum number of tokens possible to generate for a model.
|
|
194
199
|
|
|
195
200
|
Args:
|
|
196
201
|
modelname: The modelname we want to know the context size for.
|
|
@@ -250,10 +255,43 @@ def is_function_calling_model(model: str) -> bool:
|
|
|
250
255
|
def to_openai_message_dict(
|
|
251
256
|
message: ChatMessage, drop_none: bool = False, model: Optional[str] = None
|
|
252
257
|
) -> ChatCompletionMessageParam:
|
|
253
|
-
"""Convert
|
|
258
|
+
"""Convert a ChatMessage to an OpenAI message dict."""
|
|
259
|
+
content = []
|
|
260
|
+
content_txt = ""
|
|
261
|
+
for block in message.blocks:
|
|
262
|
+
if isinstance(block, TextBlock):
|
|
263
|
+
if message.role.value in ("assistant", "tool", "system"):
|
|
264
|
+
# Despite the docs say otherwise, when role is ASSISTANT, SYSTEM
|
|
265
|
+
# or TOOL, 'content' cannot be a list and must be string instead.
|
|
266
|
+
content_txt += block.text
|
|
267
|
+
else:
|
|
268
|
+
content.append({"type": "text", "text": block.text})
|
|
269
|
+
elif isinstance(block, ImageBlock):
|
|
270
|
+
if block.url:
|
|
271
|
+
content.append(
|
|
272
|
+
{"type": "image_url", "image_url": {"url": str(block.url)}}
|
|
273
|
+
)
|
|
274
|
+
else:
|
|
275
|
+
img_bytes = block.resolve_image(as_base64=True).read()
|
|
276
|
+
img_str = img_bytes.decode("utf-8")
|
|
277
|
+
content.append(
|
|
278
|
+
{
|
|
279
|
+
"type": "image_url",
|
|
280
|
+
"image_url": {
|
|
281
|
+
"url": f"data:{block.image_mimetype};base64,{img_str}",
|
|
282
|
+
"detail": block.detail or "low",
|
|
283
|
+
},
|
|
284
|
+
}
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
msg = f"Unsupported content block type: {type(block).__name__}"
|
|
288
|
+
raise ValueError(msg)
|
|
289
|
+
|
|
254
290
|
message_dict = {
|
|
255
291
|
"role": message.role.value,
|
|
256
|
-
"content":
|
|
292
|
+
"content": content_txt
|
|
293
|
+
if message.role.value in ("assistant", "tool", "system")
|
|
294
|
+
else content,
|
|
257
295
|
}
|
|
258
296
|
|
|
259
297
|
# TODO: O1 models do not support system prompts
|
|
@@ -314,7 +352,7 @@ def from_openai_token_logprob(
|
|
|
314
352
|
LogProb(token=el.token, logprob=el.logprob, bytes=el.bytes or [])
|
|
315
353
|
for el in openai_token_logprob.top_logprobs
|
|
316
354
|
]
|
|
317
|
-
except Exception
|
|
355
|
+
except Exception:
|
|
318
356
|
print(openai_token_logprob)
|
|
319
357
|
raise
|
|
320
358
|
return result
|
|
@@ -332,7 +370,7 @@ def from_openai_token_logprobs(
|
|
|
332
370
|
|
|
333
371
|
|
|
334
372
|
def from_openai_completion_logprob(
|
|
335
|
-
openai_completion_logprob: Dict[str, float]
|
|
373
|
+
openai_completion_logprob: Dict[str, float],
|
|
336
374
|
) -> List[LogProb]:
|
|
337
375
|
"""Convert openai completion logprobs to generic list of LogProb."""
|
|
338
376
|
return [
|
|
@@ -354,11 +392,6 @@ def from_openai_completion_logprobs(
|
|
|
354
392
|
return result
|
|
355
393
|
|
|
356
394
|
|
|
357
|
-
def from_openai_completion(openai_completion: Completion) -> CompletionResponse:
|
|
358
|
-
"""Convert openai completion to CompletionResponse."""
|
|
359
|
-
text = openai_completion.choices[0].text
|
|
360
|
-
|
|
361
|
-
|
|
362
395
|
def from_openai_messages(
|
|
363
396
|
openai_messages: Sequence[ChatCompletionMessage],
|
|
364
397
|
) -> List[ChatMessage]:
|
|
@@ -370,13 +403,32 @@ def from_openai_message_dict(message_dict: dict) -> ChatMessage:
|
|
|
370
403
|
"""Convert openai message dict to generic message."""
|
|
371
404
|
role = message_dict["role"]
|
|
372
405
|
# NOTE: Azure OpenAI returns function calling messages without a content key
|
|
373
|
-
content = message_dict.get("content"
|
|
406
|
+
content = message_dict.get("content")
|
|
407
|
+
blocks = []
|
|
408
|
+
if isinstance(content, list):
|
|
409
|
+
for elem in content:
|
|
410
|
+
t = elem.get("type")
|
|
411
|
+
if t == "text":
|
|
412
|
+
blocks.append(TextBlock(text=elem.get("text")))
|
|
413
|
+
elif t == "image_url":
|
|
414
|
+
img = elem["image_url"]["url"]
|
|
415
|
+
detail = elem["image_url"]["detail"]
|
|
416
|
+
if img.startswith("data:"):
|
|
417
|
+
blocks.append(ImageBlock(image=img, detail=detail))
|
|
418
|
+
else:
|
|
419
|
+
blocks.append(ImageBlock(url=img, detail=detail))
|
|
420
|
+
else:
|
|
421
|
+
msg = f"Unsupported message type: {t}"
|
|
422
|
+
raise ValueError(msg)
|
|
423
|
+
content = None
|
|
374
424
|
|
|
375
425
|
additional_kwargs = message_dict.copy()
|
|
376
426
|
additional_kwargs.pop("role")
|
|
377
427
|
additional_kwargs.pop("content", None)
|
|
378
428
|
|
|
379
|
-
return ChatMessage(
|
|
429
|
+
return ChatMessage(
|
|
430
|
+
role=role, content=content, additional_kwargs=additional_kwargs, blocks=blocks
|
|
431
|
+
)
|
|
380
432
|
|
|
381
433
|
|
|
382
434
|
def from_openai_message_dicts(message_dicts: Sequence[dict]) -> List[ChatMessage]:
|
|
@@ -386,7 +438,8 @@ def from_openai_message_dicts(message_dicts: Sequence[dict]) -> List[ChatMessage
|
|
|
386
438
|
|
|
387
439
|
@deprecated("Deprecated in favor of `to_openai_tool`, which should be used instead.")
|
|
388
440
|
def to_openai_function(pydantic_class: Type[BaseModel]) -> Dict[str, Any]:
|
|
389
|
-
"""
|
|
441
|
+
"""
|
|
442
|
+
Deprecated in favor of `to_openai_tool`.
|
|
390
443
|
|
|
391
444
|
Convert pydantic class to OpenAI function.
|
|
392
445
|
"""
|
|
@@ -412,7 +465,8 @@ def resolve_openai_credentials(
|
|
|
412
465
|
api_base: Optional[str] = None,
|
|
413
466
|
api_version: Optional[str] = None,
|
|
414
467
|
) -> Tuple[Optional[str], str, str]:
|
|
415
|
-
"""
|
|
468
|
+
"""
|
|
469
|
+
"Resolve OpenAI credentials.
|
|
416
470
|
|
|
417
471
|
The order of precedence is:
|
|
418
472
|
1. param
|
|
@@ -443,7 +497,8 @@ def validate_openai_api_key(api_key: Optional[str] = None) -> None:
|
|
|
443
497
|
|
|
444
498
|
|
|
445
499
|
def resolve_tool_choice(tool_choice: Union[str, dict] = "auto") -> Union[str, dict]:
|
|
446
|
-
"""
|
|
500
|
+
"""
|
|
501
|
+
Resolve tool choice.
|
|
447
502
|
|
|
448
503
|
If tool_choice is a function name string, return the appropriate dict.
|
|
449
504
|
"""
|
|
@@ -29,12 +29,12 @@ exclude = ["**/BUILD"]
|
|
|
29
29
|
license = "MIT"
|
|
30
30
|
name = "llama-index-llms-openai"
|
|
31
31
|
readme = "README.md"
|
|
32
|
-
version = "0.3.
|
|
32
|
+
version = "0.3.4"
|
|
33
33
|
|
|
34
34
|
[tool.poetry.dependencies]
|
|
35
35
|
python = ">=3.9,<4.0"
|
|
36
|
-
openai = "^1.
|
|
37
|
-
llama-index-core = "^0.12.
|
|
36
|
+
openai = "^1.57.1"
|
|
37
|
+
llama-index-core = "^0.12.4"
|
|
38
38
|
|
|
39
39
|
[tool.poetry.group.dev.dependencies]
|
|
40
40
|
ipython = "8.10.0"
|
|
@@ -63,3 +63,6 @@ version = ">=v2.2.6"
|
|
|
63
63
|
|
|
64
64
|
[[tool.poetry.packages]]
|
|
65
65
|
include = "llama_index/"
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint.flake8-pytest-style]
|
|
68
|
+
fixture-parentheses = true
|
|
File without changes
|
{llama_index_llms_openai-0.3.2 → llama_index_llms_openai-0.3.4}/llama_index/llms/openai/__init__.py
RENAMED
|
File without changes
|