Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,37 @@
from langchain_openai_api_bridge.chat_completion.chat_completion_chunk_object_factory import (
create_chat_completion_chunk_object,
)
from langchain_openai_api_bridge.chat_completion.content_adapter import (
to_string_content,
)
from langchain_openai_api_bridge.core.types.openai import (
OpenAIChatCompletionChunkChoice,
OpenAIChatCompletionChunkObject,
OpenAIChatMessage,
)
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk, Choice, ChoiceDelta, ChoiceDeltaFunctionCall


def to_openai_chat_message(
event: StreamEvent,
role: str = "assistant",
) -> OpenAIChatMessage:
content = event["data"]["chunk"].content
return OpenAIChatMessage(content=to_string_content(content), role=role)
) -> ChoiceDelta:
if getattr(event["data"]["chunk"], "tool_call_chunks", None):
function_call = ChoiceDeltaFunctionCall(
name=event["data"]["chunk"].tool_call_chunks[0]["name"],
arguments=event["data"]["chunk"].tool_call_chunks[0]["args"],
)
else:
function_call = None

return ChoiceDelta(
content=event["data"]["chunk"].content,
role=role,
function_call=function_call,
)


def to_openai_chat_completion_chunk_choice(
event: StreamEvent,
index: int = 0,
role: str = "assistant",
role: Optional[str] = None,
finish_reason: Optional[str] = None,
) -> OpenAIChatCompletionChunkChoice:
) -> Choice:
message = to_openai_chat_message(event, role)

return OpenAIChatCompletionChunkChoice(
return Choice(
index=index,
delta=message,
finish_reason=finish_reason,
Expand All @@ -42,9 +46,9 @@ def to_openai_chat_completion_chunk_object(
id: str = "",
model: str = "",
system_fingerprint: Optional[str] = None,
role: str = "assistant",
role: Optional[str] = None,
finish_reason: Optional[str] = None,
) -> OpenAIChatCompletionChunkObject:
) -> ChatCompletionChunk:

choice1 = to_openai_chat_completion_chunk_choice(
event, index=0, role=role, finish_reason=finish_reason
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import time
from typing import Dict, List, Optional
from typing import List, Literal, Optional

from langchain_openai_api_bridge.core.types.openai import (
OpenAIChatCompletionChunkChoice,
OpenAIChatCompletionChunkObject,
)
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk, Choice


def create_chat_completion_chunk_object(
id: str,
model: str,
system_fingerprint: Optional[str],
choices: List[OpenAIChatCompletionChunkChoice] = [],
) -> OpenAIChatCompletionChunkObject:
return OpenAIChatCompletionChunkObject(
choices: List[Choice] = [],
) -> ChatCompletionChunk:
return ChatCompletionChunk(
id=id,
object="chat.completion.chunk",
created=int(time.time()),
Expand All @@ -25,18 +22,24 @@ def create_chat_completion_chunk_object(

def create_final_chat_completion_chunk_choice(
index: int,
) -> OpenAIChatCompletionChunkChoice:
return OpenAIChatCompletionChunkChoice(index=index, delta={}, finish_reason="stop")
finish_reason: Literal["stop", "tool_calls"],
) -> Choice:
return Choice(
index=index,
delta={},
finish_reason=finish_reason,
)


def create_final_chat_completion_chunk_object(
id: str,
model: str = "",
system_fingerprint: Optional[str] = None,
) -> Dict:
finish_reason: Literal["stop", "tool_calls"] = "stop",
) -> ChatCompletionChunk:
return create_chat_completion_chunk_object(
id=id,
model=model,
system_fingerprint=system_fingerprint,
choices=[create_final_chat_completion_chunk_choice(index=0)],
choices=[create_final_chat_completion_chunk_choice(index=0, finish_reason=finish_reason)],
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from langchain_openai_api_bridge.chat_completion.langchain_stream_adapter import (
LangchainStreamAdapter,
)
from langchain_openai_api_bridge.core.types.openai import OpenAIChatMessage
from langchain_openai_api_bridge.core.utils.pydantic_async_iterator import ato_dict
from openai.types.chat import ChatCompletionMessage


class ChatCompletionCompatibleAPI:
Expand Down Expand Up @@ -39,7 +39,7 @@ def __init__(
self.agent = agent
self.event_adapter = event_adapter

async def astream(self, messages: List[OpenAIChatMessage]) -> AsyncIterator[dict]:
async def astream(self, messages: List[ChatCompletionMessage]) -> AsyncIterator[dict]:
async with self.agent as runnable:
input = self.__to_input(runnable, messages)
astream_event = runnable.astream_events(
Expand All @@ -51,7 +51,7 @@ async def astream(self, messages: List[OpenAIChatMessage]) -> AsyncIterator[dict
):
yield it

async def ainvoke(self, messages: List[OpenAIChatMessage]) -> dict:
async def ainvoke(self, messages: List[ChatCompletionMessage]) -> dict:
async with self.agent as runnable:
input = self.__to_input(runnable, messages)
result = await runnable.ainvoke(
Expand All @@ -60,16 +60,16 @@ async def ainvoke(self, messages: List[OpenAIChatMessage]) -> dict:

return self.invoke_adapter.to_chat_completion_object(result).model_dump()

def __to_input(self, runnable: Runnable, messages: List[OpenAIChatMessage]):
def __to_input(self, runnable: Runnable, messages: List[ChatCompletionMessage]):
if isinstance(runnable, CompiledStateGraph):
return self.__to_react_agent_input(messages)
else:
return self.__to_chat_model_input(messages)

def __to_react_agent_input(self, messages: List[OpenAIChatMessage]):
def __to_react_agent_input(self, messages: List[ChatCompletionMessage]):
return {
"messages": [message.model_dump() for message in messages],
"messages": [message for message in messages],
}

def __to_chat_model_input(self, messages: List[OpenAIChatMessage]):
return [message.model_dump() for message in messages]
def __to_chat_model_input(self, messages: List[ChatCompletionMessage]):
return [message for message in messages]
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import time
from typing import List, Optional

from langchain_openai_api_bridge.core.types.openai import (
OpenAIChatCompletionChoice,
OpenAIChatCompletionObject,
OpenAIChatCompletionUsage,
)
from openai.types.chat.chat_completion import ChatCompletion, Choice, CompletionUsage


class ChatCompletionObjectFactory:
def create(
id: str,
model: str,
choices: List[OpenAIChatCompletionChoice] = [],
choices: List[Choice] = [],
usage: Optional[
OpenAIChatCompletionUsage
] = OpenAIChatCompletionUsage.default(),
CompletionUsage
] = CompletionUsage(completion_tokens=-1, prompt_tokens=-1, total_tokens=-1),
object: str = "chat.completion",
system_fingerprint: str = "",
created: int = None,
) -> OpenAIChatCompletionObject:
return OpenAIChatCompletionObject(
) -> ChatCompletion:
return ChatCompletion(
id=id,
object=object,
created=created if created is not None else int(time.time()),
model=model,
created=created or int(time.time()),
object=object,
system_fingerprint=system_fingerprint,
choices=choices,
usage=usage,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,43 @@
import time
from langchain_core.messages import BaseMessage
from langchain_openai.chat_models.base import _convert_message_to_dict
from openai.types.chat.chat_completion import ChatCompletion, Choice, ChatCompletionMessage

from langchain_openai_api_bridge.chat_completion.chat_completion_object_factory import (
ChatCompletionObjectFactory,
)
from langchain_openai_api_bridge.chat_completion.content_adapter import (
to_string_content,
)
from langchain_openai_api_bridge.core.role_adapter import to_openai_role
from langchain_openai_api_bridge.core.types.openai import (
OpenAIChatCompletionChoice,
OpenAIChatCompletionObject,
OpenAIChatMessage,
)
from langchain_core.messages import AIMessage
from langchain_core.runnables.utils import Output


class LangchainInvokeAdapter:
def __init__(self, llm_model: str, system_fingerprint: str = ""):
self.llm_model = llm_model
self.system_fingerprint = system_fingerprint

def to_chat_completion_object(self, invoke_result) -> OpenAIChatCompletionObject:
message = self.__create_openai_chat_message(invoke_result)
id = self.__get_id(invoke_result)
def to_chat_completion_object(self, invoke_result: Output) -> ChatCompletion:
invoke_message = invoke_result if isinstance(invoke_result, BaseMessage) else invoke_result["messages"][-1]
message = self.__create_openai_chat_message(invoke_message)
id = self.__get_id(invoke_message)

return ChatCompletionObjectFactory.create(
id=id,
model=self.llm_model,
created=int(time.time()),
object="chat.completion",
system_fingerprint=self.system_fingerprint,
choices=[
OpenAIChatCompletionChoice(
Choice(
index=0,
message=message,
finish_reason="stop",
finish_reason="tool_calls" if "tool_calls" in message else "stop",
)
],
]
)

def __get_id(self, invoke_result):
if isinstance(invoke_result, AIMessage):
return invoke_result.id
def __create_openai_chat_message(self, message: BaseMessage) -> ChatCompletionMessage:
message = _convert_message_to_dict(message)
message["role"] = "assistant"
return message

last_message = invoke_result["messages"][-1]
return last_message.id

def __create_openai_chat_message(self, invoke_result) -> OpenAIChatMessage:
if isinstance(invoke_result, AIMessage):
return OpenAIChatMessage(
role=to_openai_role(invoke_result.type),
content=to_string_content(content=invoke_result.content),
)

last_message = invoke_result["messages"][-1]
return OpenAIChatMessage(
role=to_openai_role(last_message.type),
content=to_string_content(content=last_message.content),
)
def __get_id(self, message: BaseMessage):
return message.id or ""
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
from langchain_openai_api_bridge.chat_completion.chat_completion_chunk_object_factory import (
create_final_chat_completion_chunk_object,
)
from langchain_openai_api_bridge.core.types.openai import (
OpenAIChatCompletionChunkObject,
)
from openai.types.chat.chat_completion_chunk import ChatCompletionChunk


class LangchainStreamAdapter:
Expand All @@ -23,22 +21,31 @@ async def ato_chat_completion_chunk_stream(
astream_event: AsyncIterator[StreamEvent],
id: str = "",
event_adapter=lambda event: None,
) -> AsyncIterator[OpenAIChatCompletionChunkObject]:
) -> AsyncIterator[ChatCompletionChunk]:
if id == "":
id = str(uuid.uuid4())

is_function_call_prev = is_function_call = False
role = "assistant"
async for event in astream_event:
custom_event = event_adapter(event)
event_to_process = custom_event if custom_event is not None else event
kind = event_to_process["event"]
if kind == "on_chat_model_stream" or custom_event is not None:
yield to_openai_chat_completion_chunk_object(
chat_completion_chunk = to_openai_chat_completion_chunk_object(
event=event_to_process,
id=id,
model=self.llm_model,
system_fingerprint=self.system_fingerprint,
role=role,
)
role = None
yield chat_completion_chunk
is_function_call = is_function_call or any(choice.delta.function_call for choice in chat_completion_chunk.choices)
elif kind == "on_chat_model_end":
is_function_call_prev, is_function_call = is_function_call, False

stop_chunk = create_final_chat_completion_chunk_object(
id=id, model=self.llm_model
id=id, model=self.llm_model, finish_reason="tool_calls" if is_function_call_prev else "stop"
)
yield stop_chunk
3 changes: 3 additions & 0 deletions langchain_openai_api_bridge/core/create_agent_dto.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Optional
from pydantic import BaseModel
from openai.types.chat import ChatCompletionToolChoiceOptionParam, ChatCompletionToolParam


class CreateAgentDto(BaseModel):
Expand All @@ -9,3 +10,5 @@ class CreateAgentDto(BaseModel):
max_tokens: Optional[int] = None
assistant_id: Optional[str] = ""
thread_id: Optional[str] = ""
tools: list[ChatCompletionToolParam] = []
tool_choice: ChatCompletionToolChoiceOptionParam = "none"
12 changes: 0 additions & 12 deletions langchain_openai_api_bridge/core/types/openai/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
from .message import OpenAIChatMessage
from .chat_completion import (
OpenAIChatCompletionRequest,
OpenAIChatCompletionUsage,
OpenAIChatCompletionChoice,
OpenAIChatCompletionObject,
OpenAIChatCompletionChunkChoice,
OpenAIChatCompletionChunkObject,
)

__all__ = [
"OpenAIChatMessage",
"OpenAIChatCompletionRequest",
"OpenAIChatCompletionUsage",
"OpenAIChatCompletionChoice",
"OpenAIChatCompletionObject",
"OpenAIChatCompletionChunkChoice",
"OpenAIChatCompletionChunkObject",
]
Loading