1
0
mirror of https://github.com/ijaric/voice_assistant.git synced 2025-05-24 06:23:28 +00:00

Changes by aleksandr

This commit is contained in:
Григорич 2023-10-15 07:59:24 +03:00
parent d2933ad8f7
commit b9393d7072
9 changed files with 214 additions and 57 deletions

View File

@ -36,18 +36,19 @@ class OpenAIFunctions:
.order_by(orm_models.FilmWork.embeddings.cosine_distance(embedded_description.root))
.limit(5)
)
neighbours = session.scalars(stmt)
for neighbour in await neighbours:
response = await session.execute(stmt)
neighbours = response.scalars()
for neighbour in neighbours:
result.append(models.Movie(**neighbour.__dict__))
return result
except sqlalchemy.exc.SQLAlchemyError as error:
self.logger.exception("Error: %s", error)
@langchain.agents.tool
def get_movie_by_id(self, id: uuid.UUID) -> models.Movie | None:
def get_movie_by_id(self, id: uuid.UUID = None) -> models.Movie | None:
"""Provide a movie data by movie id."""
self.logger.info("Request to get movie by id: %s", id)
return None
# self.logger.info("Request to get movie by id: %s", id)
return f"hello world {id}"
@langchain.agents.tool
def get_similar_movies(self, id: uuid.UUID) -> list[models.Movie] | None:

View File

@ -26,7 +26,7 @@ class EmbeddingRepository:
) # type: ignore[reportGeneralTypeIssues]
return models.Embedding(**response["data"][0]["embedding"])
except openai.error.OpenAIError:
self.logger.exception("Failed to get async embedding for: %s", text)
self.logger.exception("Failed to get sync embedding for: %s", text)
async def aget_embedding(self, text: str, model: str = "text-embedding-ada-002") -> models.Embedding | None:
"""Get the embedding for a given text.[Async]"""

View File

@ -1,5 +1,6 @@
import asyncio
import logging
import typing
import uuid
import fastapi
@ -11,7 +12,7 @@ import langchain.prompts
import langchain.schema
import langchain.tools.render
import assistant.lib.models.movies as movies
import lib.models as models
import lib.agent.openai_functions as openai_functions
import lib.app.settings as app_settings
@ -23,6 +24,121 @@ class AgentService:
def __init__(self, settings: app_settings.Settings, tools: openai_functions.OpenAIFunctions) -> None:
self.settings = settings
self.tools = tools
self.llm = langchain.chat_models.ChatOpenAI(
temperature=self.settings.openai.agent_temperature,
openai_api_key=self.settings.openai.api_key.get_secret_value()
)
self.chat_repository = chat_repository
self.logger = logging.getLogger(__name__)
async def get_chat_session_id(self, request: models.RequestLastSessionId) -> uuid.UUID:
session_id = self.chat_repository.get_last_session_id(request)
if not session_id:
session_id = uuid.uuid4()
return session_id
async def artem_process_request(self, request: models.AgentCreateRequestModel) -> models.AgentCreateResponseModel:
# Get session ID
session_request = models.RequestLastSessionId(
channel=request.channel,
user_id=request.user_id,
minutes_ago=3
)
session_id = await self.chat_repository.get_last_session_id(session_request)
if not session_id:
print("NO PREVIOUS CHATS")
session_id = uuid.uuid4()
print("FOUND CHAT:", )
print("SID:", session_id)
tools = [
langchain.tools.Tool(
name="GetMovieByDescription",
func=self.tools.get_movie_by_description,
coroutine=self.tools.get_movie_by_description,
description="Get a movie by description"
),
]
llm = langchain.chat_models.ChatOpenAI(temperature=self.settings.openai.agent_temperature, openai_api_key=self.settings.openai.api_key.get_secret_value())
chat_history = langchain.memory.ChatMessageHistory()
# chat_history = []
chat_history_name = f"{chat_history=}".partition("=")[0]
request_chat_history = models.RequestChatHistory(session_id=session_id)
chat_history_source = await self.chat_repository.get_messages_by_sid(request_chat_history)
for entry in chat_history_source:
# chat_history.append(langchain.schema.messages.HumanMessage(content=first_question))
# chat_history.append(langchain.schema.messages.AIMessage(content=first_result["output"]))
if entry.content["role"] == "user":
chat_history.append(langchain.schema.messages.HumanMessage(content=entry.content["content"]))
elif entry.content["role"] == "agent":
chat_history.append(langchain.schema.messages.AIMessage(content=entry.content["content"]))
# chat_history = [entry.model_dump() for entry in chat_history_source]
memory_buffer = langchain.memory.ConversationBufferMemory(memory_key=chat_history_name,chat_memory=chat_history)
print("CHAT HISTORY:", chat_history)
# chat_history_name = f"{chat_history=}".partition("=")[0]
prompt = langchain.prompts.ChatPromptTemplate.from_messages(
[
(
"system",
"Act as an advanced AI assistant with extensive capabilities, you have a vast knowledge base about movies and their related aspects. If you are asked about a movie, please use provided functions to retrive data about movies. You can receive a question in any language. Translate it into English. If you don't know the answer, just say that you don't know, don't try to make up an answer. Be concise. ",
),
langchain.prompts.MessagesPlaceholder(variable_name=chat_history_name),
("user", "{input}"),
langchain.prompts.MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)
llm_with_tools = llm.bind(
functions=[langchain.tools.render.format_tool_to_openai_function(tool) for tool in tools]
)
agent = (
{
"input": lambda _: _["input"],
"agent_scratchpad": lambda _: langchain.agents.format_scratchpad.format_to_openai_functions(
_["intermediate_steps"]
),
"chat_history": lambda _: _["chat_history"],
}
| prompt
| llm_with_tools
| langchain.agents.output_parsers.OpenAIFunctionsAgentOutputParser()
)
agent_executor = langchain.agents.AgentExecutor(agent=agent, tools=tools, verbose=True, memory=memory_buffer)
response = await agent_executor.ainvoke({"input": request.text, "chat_history": chat_history})
print("AI RESPONSE:", response)
user_request = models.RequestChatMessage(
session_id=session_id,
user_id=request.user_id,
channel=request.channel,
message={"role": "user", "content": request.text}
)
ai_response = models.RequestChatMessage(
session_id=session_id,
user_id=request.user_id,
channel=request.channel,
message={"role": "assistant", "content": response["output"]}
)
await self.chat_repository.add_message(user_request)
await self.chat_repository.add_message(ai_response)
return response
# TODO: Добавить запрос для процессинга запроса с памятью+
# TODO: Улучшить промпт+
# TODO: Возможно, надо добавить Chain на перевод
async def process_request(self, request: models.AgentCreateRequestModel) -> models.AgentCreateResponseModel:
@ -55,26 +171,70 @@ class AgentService:
return response_model
return await agent_executor.ainvoke({"input": first_question, "chat_history": chat_history})
async def main():
import lib.agent.repositories as agent_repositories
import lib.clients as clients
postgres_client = clients.AsyncPostgresClient(app_settings.Settings())
embedding_repository = agent_repositories.EmbeddingRepository(app_settings.Settings())
chat_repository = _chat_repository.ChatHistoryRepository(postgres_client.get_async_session())
agent_service = AgentService(
settings=app_settings.Settings(),
tools=openai_functions.OpenAIFunctions(
repository=embedding_repository,
pg_async_session=postgres_client.get_async_session(),
),
chat_repository=chat_repository
)
# question = "What is the movie about a famous country singer meet a talented singer and songwriter who works as a waitress?"
request_1 = models.AgentCreateRequestModel(
channel="telegram",
user_id="123",
text="What is the movie about a famous country singer meet a talented singer and songwriter who works as a waitress?"
)
request_2 = models.AgentCreateRequestModel(
channel="telegram",
user_id="123",
text="So what is the rating of the movie? Do you recommend it?"
)
request_3 = models.AgentCreateRequestModel(
channel="telegram",
user_id="123",
text="What are the similar movies?"
)
response = await agent_service.artem_process_request(request_1)
response = await agent_service.artem_process_request(request_2)
response = await agent_service.artem_process_request(request_3)
# async def main():
# agent_executor = langchain.agents.AgentExecutor(agent=agent, tools=tools, verbose=True)
# # first_question = "What is the movie where halfling bring the ring to the volcano?"
# first_question = (
# "What is the movie about a famous country singer meet a talented singer and songwriter who works as a waitress?"
# )
# second_question = "So what is the rating of the movie? Do you recommend it?"
# third_question = "What are the similar movies?"
# first_result = await agent_executor.ainvoke({"input": first_question, "chat_history": chat_history})
# chat_history.append(langchain.schema.messages.HumanMessage(content=first_question))
# chat_history.append(langchain.schema.messages.AIMessage(content=first_result["output"]))
# second_result = await agent_executor.ainvoke({"input": second_question, "chat_history": chat_history})
# chat_history.append(langchain.schema.messages.HumanMessage(content=second_question))
# chat_history.append(langchain.schema.messages.AIMessage(content=second_result["output"]))
# final_result = await agent_executor.ainvoke({"input": third_question, "chat_history": chat_history})
# if __name__ == "__main__":
# asyncio.run(main())
# response = await agent_service.artem_process_request(question)
# question = "Highly Rated Titanic Movies"
# request = models.AgentCreateRequestModel(text=question)
# film_results = await agent_service.process_request(request=request)
# result = [agent_service.tools.get_movie_by_id(id=film.id) for film in film_results]
# agent_executor = langchain.agents.AgentExecutor(agent=agent, tools=tools, verbose=True)
#
# # first_question = "What is the movie where halfling bring the ring to the volcano?"
# first_question = (
# "What is the movie about a famous country singer meet a talented singer and songwriter who works as a waitress?"
# )
# second_question = "So what is the rating of the movie? Do you recommend it?"
# third_question = "What are the similar movies?"
# first_result = await agent_executor.ainvoke({"input": first_question, "chat_history": chat_history})
# chat_history.append(langchain.schema.messages.HumanMessage(content=first_question))
# chat_history.append(langchain.schema.messages.AIMessage(content=first_result["output"]))
# second_result = await agent_executor.ainvoke({"input": second_question, "chat_history": chat_history})
# chat_history.append(langchain.schema.messages.HumanMessage(content=second_question))
# chat_history.append(langchain.schema.messages.AIMessage(content=second_result["output"]))
# final_result = await agent_executor.ainvoke({"input": third_question, "chat_history": chat_history})
if __name__ == "__main__":
asyncio.run(main())

View File

@ -5,18 +5,18 @@ import fastapi
import lib.stt.services as stt_services
# import lib.tts.services as tts_service
# import lib.models as models
import lib.tts.services as tts_service
import lib.models as models
class VoiceResponseHandler:
def __init__(
self,
stt: stt_services.SpeechService,
# tts: tts_service.TTSService,
tts: tts_service.TTSService,
):
self.stt = stt
# self.tts = tts
self.tts = tts
self.router = fastapi.APIRouter()
self.router.add_api_route(
"/",
@ -36,10 +36,10 @@ class VoiceResponseHandler:
# TODO: Добавить обработку текста через клиента openai
# TODO: Добавить синтез речи через клиента tts
# TODO: Заменить заглушку на реальный ответ
# response = await self.tts.get_audio_as_bytes(
# models.TTSCreateRequestModel(
# text=voice_text,
# )
# )
# return fastapi.responses.StreamingResponse(io.BytesIO(response.audio_content), media_type="audio/ogg")
return fastapi.responses.StreamingResponse(io.BytesIO(voice), media_type="audio/ogg")
response = await self.tts.get_audio_as_bytes(
models.TTSCreateRequestModel(
text=voice_text,
)
)
return fastapi.responses.StreamingResponse(io.BytesIO(response.audio_content), media_type="audio/ogg")
# return fastapi.responses.StreamingResponse(io.BytesIO(voice), media_type="audio/ogg")

View File

@ -117,7 +117,7 @@ class Application:
models.VoiceModelProvidersEnum.ELEVEN_LABS: tts_eleven_labs_repository,
},
)
# Handlers
logger.info("Initializing handlers")
@ -127,7 +127,7 @@ class Application:
# TODO: объявить сервисы tts и openai и добавить их в voice_response_handler
voice_response_handler = api_v1_handlers.VoiceResponseHandler(
stt=stt_service,
# tts=tts_service, # TODO
tts=tts_service,
).router
logger.info("Creating application")

View File

@ -16,3 +16,4 @@ class OpenaiSettings(pydantic_settings.BaseSettings):
default=..., validation_alias=pydantic.AliasChoices("api_key", "openai_api_key")
)
stt_model: str = "whisper-1"
agent_temperature: float = 0.7

View File

@ -41,4 +41,4 @@ class PostgresSettings(pydantic_settings.BaseSettings):
@property
def dsn_as_safe_url(self) -> str:
return f"{self.driver}://{self.user}:***@{self.host}:{self.port}"
return f"{self.driver}://{self.user}:***@{self.host}:{self.port}/{self.db_name}"

View File

@ -3,18 +3,19 @@ from .embedding import Embedding
from .movies import Movie
from .token import Token
from .tts import *
from .agent import *
__all__ = ["Embedding", "Message", "Movie", "RequestChatHistory", "RequestChatMessage", "RequestLastSessionId", "Token"]
# __all__ = ["Embedding", "Message", "Movie", "RequestChatHistory", "RequestChatMessage", "RequestLastSessionId", "Token"]
__all__ = [
"AVAILABLE_MODELS_TYPE",
"Base",
# "Base",
"BaseLanguageCodesEnum",
"BaseVoiceModel",
"ElevenLabsLanguageCodesEnum",
"ElevenLabsListVoiceModelsModel",
"ElevenLabsVoiceModel",
"IdCreatedUpdatedBaseMixin",
# "IdCreatedUpdatedBaseMixin",
"LANGUAGE_CODES_ENUM_TYPE",
"LIST_VOICE_MODELS_TYPE",
"TTSCreateRequestModel",
@ -25,4 +26,5 @@ __all__ = [
"YandexLanguageCodesEnum",
"YandexListVoiceModelsModel",
"YandexVoiceModel",
"AgentCreateRequestModel",
]

View File

@ -13,7 +13,7 @@ profile = "black"
py_version = "311"
[tool.poetry]
authors = ["ijaric@gmail.com", "jsdio@jsdio.ru"]
authors = ["jsdio@jsdio.ru"]
description = ""
name = "fastapi_project"
readme = "README.md"
@ -22,29 +22,23 @@ version = "0.1.0"
[tool.poetry.dependencies]
alembic = "^1.12.0"
asyncpg = "^0.28.0"
dill = "^0.3.7"
faker = "^19.10.0"
fastapi = "0.103.1"
greenlet = "^2.0.2"
httpx = "^0.25.0"
langchain = "^0.0.312"
openai = "^0.28.1"
pgvector = "^0.2.3"
multidict = "^6.0.4"
openai = "^0.28.1"
orjson = "3.9.7"
orjson = "^3.9.7"
psycopg2-binary = "^2.9.9"
pydantic = {extras = ["email"], version = "^2.3.0"}
pydantic-settings = "^2.0.3"
pytest = "^7.4.2"
pytest-asyncio = "^0.21.1"
python = "^3.11"
python-jose = "^3.3.0"
python-magic = "^0.4.27"
python-multipart = "^0.0.6"
sqlalchemy = "^2.0.20"
uvicorn = "^0.23.2"
wrapt = "^1.15.0"
pgvector = "^0.2.3"
python-magic = "^0.4.27"
openai = "^0.28.1"
python-multipart = "^0.0.6"
[tool.poetry.dev-dependencies]
black = "^23.7.0"
@ -98,9 +92,8 @@ variable-rgx = "^_{0,2}[a-z][a-z0-9_]*$"
[tool.pyright]
exclude = [
".venv",
"alembic"
".pytest_cache",
".venv"
]
pythonPlatform = "All"
pythonVersion = "3.11"