diff --git a/.github/workflows/generate-application-server-client.yml b/.github/workflows/generate-application-server-client.yml index 1c0ad012..0e22003e 100644 --- a/.github/workflows/generate-application-server-client.yml +++ b/.github/workflows/generate-application-server-client.yml @@ -83,4 +83,4 @@ jobs: run: | echo "Removing the autocommit-openapi label..." curl --silent --fail-with-body -X DELETE -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/autocommit-openapi + https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/autocommit-openapi \ No newline at end of file diff --git a/.github/workflows/generate-intelligence-service-client.yml b/.github/workflows/generate-intelligence-service-client.yml index 54842180..88b05152 100644 --- a/.github/workflows/generate-intelligence-service-client.yml +++ b/.github/workflows/generate-intelligence-service-client.yml @@ -57,7 +57,7 @@ jobs: - name: Install Python dependencies working-directory: server/intelligence-service - run: poetry install --no-interaction --no-root + run: poetry lock --no-update && poetry install --no-interaction --no-root - name: Generate API client for the application server working-directory: server/intelligence-service @@ -97,4 +97,4 @@ jobs: run: | echo "Removing the autocommit-openapi label..." curl --silent --fail-with-body -X DELETE -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ - https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/autocommit-openapi + https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/autocommit-openapi \ No newline at end of file diff --git a/.github/workflows/black.yml b/.github/workflows/intelligence-service-qa.yml similarity index 80% rename from .github/workflows/black.yml rename to .github/workflows/intelligence-service-qa.yml index fba24f95..a73cabfd 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/intelligence-service-qa.yml @@ -1,9 +1,17 @@ -name: Lint +name: Intelligence Service QA -on: [pull_request] +on: + pull_request: + paths: + - "server/intelligence-service/**" + push: + paths: + - "server/intelligence-service/**" + branches: [develop] jobs: lint: + name: Code Quality Checks runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/package.json b/package.json index 59110344..e32cd3d8 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,10 @@ "generate:api:application-server": "npm run generate:api:application-server-specs && npm run generate:api:application-server:clean && npm run generate:api:application-server-client", "generate:api:intelligence-service": "npm run generate:api:intelligence-service:clean && npm run generate:api:intelligence-service-specs && npm run generate:api:intelligence-service-client", "generate:api": "npm run generate:api:intelligence-service && npm run generate:api:application-server", - "prettier:java:check": "prettier --check server/application-server/src/**/*.java", - "prettier:java:write": "prettier --write server/application-server/src/**/*.java", + "format:java:check": "prettier --check server/application-server/src/**/*.java", + "format:java:write": "prettier --write server/application-server/src/**/*.java", + "format:python:check": "cd server/intelligence-service/ && poetry run black --check .", + "format:python:write": "cd server/intelligence-service/ && poetry run black .", "db:changelog:diff": "cd server/application-server && docker compose up -d postgres && mvn liquibase:diff && docker compose down postgres" }, "devDependencies": { diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DefaultApi.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DefaultApi.java index 0158bbd6..946825f3 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DefaultApi.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/api/DefaultApi.java @@ -42,7 +42,7 @@ public DefaultApi(ApiClient apiClient) { } /** - * Get a response from an LLM to a chat message. + * Start and continue a chat session with an LLM. * *

200 - Successful Response *

422 - Validation Error @@ -55,7 +55,7 @@ public ChatResponse chatChatPost(ChatRequest chatRequest) throws RestClientExcep } /** - * Get a response from an LLM to a chat message. + * Start and continue a chat session with an LLM. * *

200 - Successful Response *

422 - Validation Error diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/ChatRequest.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/ChatRequest.java index 70234d22..2a35c7bd 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/ChatRequest.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/ChatRequest.java @@ -20,6 +20,10 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonValue; +import org.openapitools.jackson.nullable.JsonNullable; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.openapitools.jackson.nullable.JsonNullable; +import java.util.NoSuchElementException; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonTypeName; import org.hibernate.validator.constraints.*; @@ -28,13 +32,17 @@ * ChatRequest */ @JsonPropertyOrder({ - ChatRequest.JSON_PROPERTY_MESSAGE + ChatRequest.JSON_PROPERTY_MESSAGE, + ChatRequest.JSON_PROPERTY_THREAD_ID }) @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") public class ChatRequest { public static final String JSON_PROPERTY_MESSAGE = "message"; private String message; + public static final String JSON_PROPERTY_THREAD_ID = "thread_id"; + private JsonNullable threadId = JsonNullable.undefined(); + public ChatRequest() { } @@ -63,6 +71,39 @@ public void setMessage(String message) { this.message = message; } + public ChatRequest threadId(String threadId) { + this.threadId = JsonNullable.of(threadId); + + return this; + } + + /** + * Get threadId + * @return threadId + */ + @jakarta.annotation.Nullable + @JsonIgnore + + public String getThreadId() { + return threadId.orElse(null); + } + + @JsonProperty(JSON_PROPERTY_THREAD_ID) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public JsonNullable getThreadId_JsonNullable() { + return threadId; + } + + @JsonProperty(JSON_PROPERTY_THREAD_ID) + public void setThreadId_JsonNullable(JsonNullable threadId) { + this.threadId = threadId; + } + + public void setThreadId(String threadId) { + this.threadId = JsonNullable.of(threadId); + } + @Override public boolean equals(Object o) { if (this == o) { @@ -72,12 +113,24 @@ public boolean equals(Object o) { return false; } ChatRequest chatRequest = (ChatRequest) o; - return Objects.equals(this.message, chatRequest.message); + return Objects.equals(this.message, chatRequest.message) && + equalsNullable(this.threadId, chatRequest.threadId); + } + + private static boolean equalsNullable(JsonNullable a, JsonNullable b) { + return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); } @Override public int hashCode() { - return Objects.hash(message); + return Objects.hash(message, hashCodeNullable(threadId)); + } + + private static int hashCodeNullable(JsonNullable a) { + if (a == null) { + return 1; + } + return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; } @Override @@ -85,6 +138,7 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class ChatRequest {\n"); sb.append(" message: ").append(toIndentedString(message)).append("\n"); + sb.append(" threadId: ").append(toIndentedString(threadId)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/ChatResponse.java b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/ChatResponse.java index a601104d..5f615747 100644 --- a/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/ChatResponse.java +++ b/server/application-server/src/main/java/de/tum/in/www1/hephaestus/intelligenceservice/model/ChatResponse.java @@ -20,6 +20,10 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonValue; +import org.openapitools.jackson.nullable.JsonNullable; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.openapitools.jackson.nullable.JsonNullable; +import java.util.NoSuchElementException; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonTypeName; import org.hibernate.validator.constraints.*; @@ -28,13 +32,17 @@ * ChatResponse */ @JsonPropertyOrder({ - ChatResponse.JSON_PROPERTY_RESPONSE + ChatResponse.JSON_PROPERTY_RESPONSE, + ChatResponse.JSON_PROPERTY_THREAD_ID }) @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0") public class ChatResponse { public static final String JSON_PROPERTY_RESPONSE = "response"; private String response; + public static final String JSON_PROPERTY_THREAD_ID = "thread_id"; + private JsonNullable threadId = JsonNullable.undefined(); + public ChatResponse() { } @@ -63,6 +71,39 @@ public void setResponse(String response) { this.response = response; } + public ChatResponse threadId(String threadId) { + this.threadId = JsonNullable.of(threadId); + + return this; + } + + /** + * Get threadId + * @return threadId + */ + @jakarta.annotation.Nullable + @JsonIgnore + + public String getThreadId() { + return threadId.orElse(null); + } + + @JsonProperty(JSON_PROPERTY_THREAD_ID) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + + public JsonNullable getThreadId_JsonNullable() { + return threadId; + } + + @JsonProperty(JSON_PROPERTY_THREAD_ID) + public void setThreadId_JsonNullable(JsonNullable threadId) { + this.threadId = threadId; + } + + public void setThreadId(String threadId) { + this.threadId = JsonNullable.of(threadId); + } + @Override public boolean equals(Object o) { if (this == o) { @@ -72,12 +113,24 @@ public boolean equals(Object o) { return false; } ChatResponse chatResponse = (ChatResponse) o; - return Objects.equals(this.response, chatResponse.response); + return Objects.equals(this.response, chatResponse.response) && + equalsNullable(this.threadId, chatResponse.threadId); + } + + private static boolean equalsNullable(JsonNullable a, JsonNullable b) { + return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); } @Override public int hashCode() { - return Objects.hash(response); + return Objects.hash(response, hashCodeNullable(threadId)); + } + + private static int hashCodeNullable(JsonNullable a) { + if (a == null) { + return 1; + } + return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; } @Override @@ -85,6 +138,7 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class ChatResponse {\n"); sb.append(" response: ").append(toIndentedString(response)).append("\n"); + sb.append(" threadId: ").append(toIndentedString(threadId)).append("\n"); sb.append("}"); return sb.toString(); } diff --git a/server/intelligence-service/app/config.py b/server/intelligence-service/app/config.py index 5383e680..006ea3a5 100644 --- a/server/intelligence-service/app/config.py +++ b/server/intelligence-service/app/config.py @@ -19,8 +19,11 @@ def is_openai_available(self): @property def is_azure_openai_available(self): - return bool(self.AZURE_OPENAI_API_KEY) and bool(self.AZURE_OPENAI_ENDPOINT) and bool( - self.AZURE_OPENAI_API_VERSION) + return ( + bool(self.AZURE_OPENAI_API_KEY) + and bool(self.AZURE_OPENAI_ENDPOINT) + and bool(self.AZURE_OPENAI_API_VERSION) + ) settings = Settings() diff --git a/server/intelligence-service/app/main.py b/server/intelligence-service/app/main.py index dd46f0f8..986a4cdb 100644 --- a/server/intelligence-service/app/main.py +++ b/server/intelligence-service/app/main.py @@ -1,7 +1,7 @@ from fastapi import FastAPI, HTTPException from pydantic import BaseModel -from .model import model - +from .model import start_chat as start_chat_function, chat as chat_function +from typing import Dict, Optional app = FastAPI( title="Hephaestus Intelligence Service API", @@ -10,19 +10,43 @@ contact={"name": "Felix T.J. Dietrich", "email": "felixtj.dietrich@tum.de"}, ) +# Global dictionary to store conversation states +conversations: Dict[str, dict] = {} + class ChatRequest(BaseModel): message: str + thread_id: Optional[str] = None class ChatResponse(BaseModel): response: str + thread_id: Optional[str] = None -@app.post("/chat", response_model=ChatResponse, summary="Get a response from an LLM to a chat message.") +@app.post( + "/chat", + response_model=ChatResponse, + summary="Start and continue a chat session with an LLM.", +) async def chat(request: ChatRequest): - try: - response = model.invoke(request.message) - return {"response": response.content} - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + if request.thread_id is None: + # Start a new chat session + result = start_chat_function(request.message) + thread_id = result["thread_id"] + state = result["state"] + response_message = result["response"]["messages"][-1].content + conversations[thread_id] = state + return ChatResponse(thread_id=thread_id, response=response_message) + else: + thread_id = request.thread_id + # Check if the thread_id exists + if thread_id not in conversations: + raise HTTPException(status_code=404, detail="Thread ID not found") + state = conversations[thread_id] + user_input = request.message + result = chat_function(thread_id, user_input, state) + state = result["state"] + response_message = result["response"]["messages"][-1].content + conversations[thread_id] = state + return ChatResponse(response=response_message) diff --git a/server/intelligence-service/app/model.py b/server/intelligence-service/app/model.py index ecacc241..59fb8638 100644 --- a/server/intelligence-service/app/model.py +++ b/server/intelligence-service/app/model.py @@ -1,15 +1,27 @@ import os +from .config import settings +from random import randint +from typing import Sequence +from typing_extensions import Annotated, TypedDict + +from langchain_core.messages import trim_messages, BaseMessage, HumanMessage from langchain.chat_models.base import BaseChatModel from langchain_openai import ChatOpenAI, AzureChatOpenAI +from langchain_core.prompts import ChatPromptTemplate +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +from langgraph.checkpoint.memory import MemorySaver +from langgraph.graph import START, StateGraph +from langgraph.graph.message import add_messages -from .config import settings model: BaseChatModel if os.getenv("GITHUB_ACTIONS") == "true": - class MockChatModel(): + + class MockChatModel: def invoke(self, message: str): return "Mock response" + model = MockChatModel() elif settings.is_openai_available: @@ -18,3 +30,79 @@ def invoke(self, message: str): model = AzureChatOpenAI() else: raise EnvironmentError("No LLM available") + + +class State(TypedDict): + messages: Annotated[Sequence[BaseMessage], add_messages] + step: int + + +mentor_prompt = ChatPromptTemplate.from_messages( + [ + ( + "system", + "You are an AI mentor helping a students working on the software engineering projects." + + "You need to guide the student through the set of three questions regarding their work on the project during the last week (sprint)." + + "Steps are the indicator of your current task in the conversation with the student. Your current step is {step}. Just follow the instructions and focus on the current step." + + "If your step is 0: greet the student and ask about the overall progress on the project." + + "If your step is 1: ask the student about the challenges faced during the sprint reffering to what he saied about progress." + + "If your step is 2: ask about the plan for the next sprint." + + "If your step is >2: continue the conversation trying to assist the student.", + ), + MessagesPlaceholder(variable_name="messages"), + ] +) + +workflow = StateGraph(state_schema=State) +trimmer = trim_messages( + max_tokens=400, + strategy="last", + token_counter=model, + include_system=True, + allow_partial=False, + start_on="human", +) + + +def call_model(state: State): + chain = mentor_prompt | model + trimmed_messages = trimmer.invoke(state["messages"]) + response = chain.invoke({"messages": trimmed_messages, "step": state["step"]}) + return {"messages": [response]} + + +workflow.add_edge(START, "model") +workflow.add_node("model", call_model) + +memory = MemorySaver() +app = workflow.compile(checkpointer=memory) + + +def start_chat(input_message: str): + messages = [HumanMessage(input_message)] + state = State(messages=messages, step=0) + thread_id = "chat_" + str(randint(0, 9999)) + config = {"configurable": {"thread_id": thread_id}} + + output = app.invoke( + {"messages": state["messages"], "step": state["step"]}, + config, + ) + + state["messages"] += output.get("messages", []) + return {"thread_id": thread_id, "state": state, "response": output} + + +def chat(thread_id: str, input_message: str, state: State): + config = {"configurable": {"thread_id": thread_id}} + # append the new human message to the conversation + state["messages"] += [HumanMessage(input_message)] + state["step"] += 1 + + output = app.invoke( + {"messages": state["messages"], "step": state["step"]}, + config, + ) + + state["messages"] += output.get("messages", []) + return {"state": state, "response": output} diff --git a/server/intelligence-service/openapi.yaml b/server/intelligence-service/openapi.yaml index d33d3e8d..0f802b7e 100644 --- a/server/intelligence-service/openapi.yaml +++ b/server/intelligence-service/openapi.yaml @@ -5,6 +5,11 @@ components: message: title: Message type: string + thread_id: + anyOf: + - type: string + - type: 'null' + title: Thread Id required: - message title: ChatRequest @@ -14,6 +19,11 @@ components: response: title: Response type: string + thread_id: + anyOf: + - type: string + - type: 'null' + title: Thread Id required: - response title: ChatResponse @@ -79,4 +89,4 @@ paths: schema: $ref: '#/components/schemas/HTTPValidationError' description: Validation Error - summary: Get a response from an LLM to a chat message. + summary: Start and continue a chat session with an LLM. diff --git a/server/intelligence-service/poetry.lock b/server/intelligence-service/poetry.lock index 3506f850..9d3e654f 100644 --- a/server/intelligence-service/poetry.lock +++ b/server/intelligence-service/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -769,6 +769,17 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "httpx-sse" +version = "0.4.0" +description = "Consume Server-Sent Event (SSE) messages with HTTPX." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"}, + {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"}, +] + [[package]] name = "idna" version = "3.10" @@ -983,6 +994,53 @@ files = [ [package.dependencies] langchain-core = ">=0.2.38,<0.3.0" +[[package]] +name = "langgraph" +version = "0.2.44" +description = "Building stateful, multi-actor applications with LLMs" +optional = false +python-versions = "<4.0,>=3.9.0" +files = [ + {file = "langgraph-0.2.44-py3-none-any.whl", hash = "sha256:ac03af8950efcb2ebb898bce0800c8c9bad9180f7490a73d2eaecacab7a7920a"}, + {file = "langgraph-0.2.44.tar.gz", hash = "sha256:8ae179c3a77666b0a78b273d89d8f32a012282ad49298234426a4443d559aa62"}, +] + +[package.dependencies] +langchain-core = ">=0.2.43,<0.3.0 || >0.3.0,<0.3.1 || >0.3.1,<0.3.2 || >0.3.2,<0.3.3 || >0.3.3,<0.3.4 || >0.3.4,<0.3.5 || >0.3.5,<0.3.6 || >0.3.6,<0.3.7 || >0.3.7,<0.3.8 || >0.3.8,<0.3.9 || >0.3.9,<0.3.10 || >0.3.10,<0.3.11 || >0.3.11,<0.3.12 || >0.3.12,<0.3.13 || >0.3.13,<0.3.14 || >0.3.14,<0.4.0" +langgraph-checkpoint = ">=2.0.0,<3.0.0" +langgraph-sdk = ">=0.1.32,<0.2.0" + +[[package]] +name = "langgraph-checkpoint" +version = "2.0.2" +description = "Library with base interfaces for LangGraph checkpoint savers." +optional = false +python-versions = "<4.0.0,>=3.9.0" +files = [ + {file = "langgraph_checkpoint-2.0.2-py3-none-any.whl", hash = "sha256:6e5dfd90e1fc71b91ccff75939ada1114e5d7f824df5f24c62d39bed69039ee2"}, + {file = "langgraph_checkpoint-2.0.2.tar.gz", hash = "sha256:c1d033e4e4855f580fa56830327eb86513b64ab5be527245363498e76b19a0b9"}, +] + +[package.dependencies] +langchain-core = ">=0.2.38,<0.4" +msgpack = ">=1.1.0,<2.0.0" + +[[package]] +name = "langgraph-sdk" +version = "0.1.35" +description = "SDK for interacting with LangGraph API" +optional = false +python-versions = "<4.0.0,>=3.9.0" +files = [ + {file = "langgraph_sdk-0.1.35-py3-none-any.whl", hash = "sha256:b137c324fbce96afe39cc6a189c61fc042164068f0f6f02ac8de864d8ece6e05"}, + {file = "langgraph_sdk-0.1.35.tar.gz", hash = "sha256:414cfbc172b883446197763f3645d86bbc6a5b8ce9693c1df3fb6ce7d854a994"}, +] + +[package.dependencies] +httpx = ">=0.25.2" +httpx-sse = ">=0.4.0" +orjson = ">=3.10.1" + [[package]] name = "langsmith" version = "0.1.139" @@ -1109,6 +1167,79 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "msgpack" +version = "1.1.0" +description = "MessagePack serializer" +optional = false +python-versions = ">=3.8" +files = [ + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, + {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, + {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, + {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, + {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, + {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, + {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, + {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, + {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, + {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, + {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, + {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, + {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, + {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, +] + [[package]] name = "multidict" version = "6.1.0" @@ -2529,4 +2660,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "4d8bfd77fe7d93844ac01046e3b8699f59ce1184ad9e424ea60662b6b62a4115" +content-hash = "7f7ca24dc8c94926e9d096d40d4fd244fe8adba33fae95d87b625ecdd40ec6dd" diff --git a/server/intelligence-service/pyproject.toml b/server/intelligence-service/pyproject.toml index 6bf2fbaf..e343c4dd 100644 --- a/server/intelligence-service/pyproject.toml +++ b/server/intelligence-service/pyproject.toml @@ -13,6 +13,7 @@ fastapi = {extras = ["standard"], version = "0.112.1"} pydantic-settings = "2.4.0" langchain = "0.2.15" langchain-openai = "0.1.23" +langgraph = "0.2.44" [tool.poetry.group.dev.dependencies] black = "24.10.0"