mirror of
https://github.com/langchain-ai/langgraph-fullstack-python.git
synced 2026-07-01 20:04:03 -04:00
First Commit
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
TAVILY_API_KEY=...
|
||||
|
||||
# To separate your traces from other application
|
||||
LANGSMITH_PROJECT=react-agent
|
||||
|
||||
# The following depend on your selected configuration
|
||||
|
||||
## LLM choice:
|
||||
ANTHROPIC_API_KEY=....
|
||||
FIREWORKS_API_KEY=...
|
||||
OPENAI_API_KEY=...
|
||||
@@ -0,0 +1,44 @@
|
||||
# This workflow will run integration tests for the current project once per day
|
||||
|
||||
name: Integration Tests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "37 14 * * *" # Run at 7:37 AM Pacific Time (14:37 UTC) every day
|
||||
workflow_dispatch: # Allows triggering the workflow manually in GitHub UI
|
||||
|
||||
# If another scheduled run starts while this workflow is still running,
|
||||
# cancel the earlier run in favor of the next run.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Integration Tests
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
python-version: ["3.11", "3.12"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
uv venv
|
||||
uv pip install -r pyproject.toml
|
||||
uv pip install -U pytest-asyncio vcrpy
|
||||
- name: Run integration tests
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
|
||||
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
|
||||
LANGSMITH_TRACING: true
|
||||
LANGSMITH_TEST_CACHE: tests/cassettes
|
||||
run: |
|
||||
uv run pytest tests/integration_tests
|
||||
@@ -0,0 +1,57 @@
|
||||
# This workflow will run unit tests for the current project
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
workflow_dispatch: # Allows triggering the workflow manually in GitHub UI
|
||||
|
||||
# If another push to the same PR or branch happens while this workflow is still running,
|
||||
# cancel the earlier run in favor of the next run.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Unit Tests
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
python-version: ["3.11", "3.12"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
uv venv
|
||||
uv pip install -r pyproject.toml
|
||||
- name: Lint with ruff
|
||||
run: |
|
||||
uv pip install ruff
|
||||
uv run ruff check .
|
||||
- name: Lint with mypy
|
||||
run: |
|
||||
uv pip install mypy
|
||||
uv run mypy --strict src/
|
||||
- name: Check README spelling
|
||||
uses: codespell-project/actions-codespell@v2
|
||||
with:
|
||||
ignore_words_file: .codespellignore
|
||||
path: README.md
|
||||
- name: Check code spelling
|
||||
uses: codespell-project/actions-codespell@v2
|
||||
with:
|
||||
ignore_words_file: .codespellignore
|
||||
path: src/
|
||||
- name: Run tests with pytest
|
||||
run: |
|
||||
uv pip install pytest
|
||||
uv run pytest tests/unit_tests
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
uv.lock
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 LangChain
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,64 @@
|
||||
.PHONY: all format lint test tests test_watch integration_tests docker_tests help extended_tests
|
||||
|
||||
# Default target executed when no arguments are given to make.
|
||||
all: help
|
||||
|
||||
# Define a variable for the test file path.
|
||||
TEST_FILE ?= tests/unit_tests/
|
||||
|
||||
test:
|
||||
uv run --with-editable . pytest $(TEST_FILE)
|
||||
|
||||
test_watch:
|
||||
uv run --with-editable . ptw --snapshot-update --now . -- -vv tests/unit_tests
|
||||
|
||||
test_profile:
|
||||
uv run --with-editable . pytest -vv tests/unit_tests/ --profile-svg
|
||||
|
||||
extended_tests:
|
||||
uv run --with-editable . pytest --only-extended $(TEST_FILE)
|
||||
|
||||
|
||||
######################
|
||||
# LINTING AND FORMATTING
|
||||
######################
|
||||
|
||||
# Define a variable for Python and notebook files.
|
||||
PYTHON_FILES=src/
|
||||
MYPY_CACHE=.mypy_cache
|
||||
lint format: PYTHON_FILES=.
|
||||
lint_diff format_diff: PYTHON_FILES=$(shell git diff --name-only --diff-filter=d main | grep -E '\.py$$|\.ipynb$$')
|
||||
lint_package: PYTHON_FILES=src
|
||||
lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
lint lint_diff lint_package lint_tests:
|
||||
python -m ruff check .
|
||||
[ "$(PYTHON_FILES)" = "" ] || python -m ruff format $(PYTHON_FILES) --diff
|
||||
[ "$(PYTHON_FILES)" = "" ] || python -m ruff check --select I $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || python -m mypy --strict $(PYTHON_FILES)
|
||||
[ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && python -m mypy --strict $(PYTHON_FILES) --cache-dir $(MYPY_CACHE)
|
||||
|
||||
format format_diff:
|
||||
ruff format $(PYTHON_FILES)
|
||||
ruff check --select I --fix $(PYTHON_FILES)
|
||||
|
||||
spell_check:
|
||||
codespell --toml pyproject.toml
|
||||
|
||||
spell_fix:
|
||||
codespell --toml pyproject.toml -w
|
||||
|
||||
######################
|
||||
# HELP
|
||||
######################
|
||||
|
||||
help:
|
||||
@echo '----'
|
||||
@echo 'format - run code formatters'
|
||||
@echo 'lint - run linters'
|
||||
@echo 'test - run unit tests'
|
||||
@echo 'tests - run unit tests'
|
||||
@echo 'test TEST_FILE=<test_file> - run all tests in file'
|
||||
@echo 'test_watch - run unit tests in watch mode'
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
# Full-Stack Python Chatbot with LangGraph
|
||||
|
||||
[](https://github.com/langchain-ai/langgraph-fullstack-python/actions/workflows/unit-tests.yml)
|
||||
[](https://github.com/langchain-ai/langgraph-fullstack-python/actions/workflows/integration-tests.yml)
|
||||
[](https://langgraph-studio.vercel.app/templates/open?githubUrl=https://github.com/langchain-ai/langgraph-fullstack-python)
|
||||
|
||||
This template demonstrates how to build a full-stack chatbot application using LangGraph's HTTP configuration capabilities. It showcases how to combine a React-style agent with a modern web UI, all hosted within a single LangGraph deployment.
|
||||
|
||||
## Key Features
|
||||
|
||||
- 🌐 **Single Deployment** - Host both your agent and UI in one LangGraph deployment
|
||||
- 🎨 **Modern UI** - Beautiful chat interface built with FastHTML and DaisyUI
|
||||
- 🔄 **React-Style Agent** - Intelligent chatbot using LangGraph's React agent pattern
|
||||
- 🛠️ **Easy Configuration** - Simple HTTP routing setup through `langgraph.json`
|
||||
- ⚡ **Fast Development** - Rapid prototyping with FastHTML's server-side components
|
||||
|
||||
## How It Works
|
||||
|
||||
### HTTP Configuration
|
||||
|
||||
The magic happens in `langgraph.json`, where we configure both the agent and HTTP routes:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"agent": "./src/react_agent/graph.py:graph"
|
||||
},
|
||||
"http": {
|
||||
"app": "./src/react_agent/app.py:app"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration:
|
||||
1. Defines the agent graph in `graph.py`
|
||||
2. Sets up HTTP routes through FastHTML in `app.py`
|
||||
|
||||
### FastHTML UI
|
||||
|
||||
The UI is built using FastHTML, a lightweight server-side component framework. Key features:
|
||||
|
||||
- Modern chat interface using DaisyUI components
|
||||
- Real-time message updates
|
||||
- Clean, responsive design
|
||||
|
||||
### LangGraph Agent
|
||||
|
||||
The chatbot uses LangGraph's React agent pattern, which:
|
||||
|
||||
- Processes messages through a Claude 3 model
|
||||
- Maintains conversation state
|
||||
- Can be easily extended with custom tools
|
||||
|
||||
## Getting Started
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
```bash
|
||||
pip install uv
|
||||
uv sync --dev
|
||||
```
|
||||
|
||||
Then run the local server:
|
||||
```bash
|
||||
uv run langgraph dev --no-browser
|
||||
```
|
||||
|
||||
Visit `http://localhost:2024` to interact with your chatbot!
|
||||
|
||||
## Customization
|
||||
|
||||
### Modify the Agent
|
||||
|
||||
Edit `src/react_agent/graph.py` to:
|
||||
- Change the system prompt
|
||||
- Add custom tools
|
||||
- Modify the agent's behavior
|
||||
|
||||
### Customize the UI
|
||||
|
||||
Edit `src/react_agent/app.py` to:
|
||||
- Update the chat interface
|
||||
- Add new components
|
||||
- Modify styling
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Add persistent storage for chat history
|
||||
- Implement custom tools for your agent
|
||||
- Enhance the UI with additional features
|
||||
- Deploy to production using LangGraph Platform
|
||||
|
||||
For more examples and detailed documentation:
|
||||
- [LangGraph Documentation](https://langchain-ai.github.io/langgraph)
|
||||
- [FastHTML Documentation](https://fasthtml.readme.io)
|
||||
- [DaisyUI Components](https://daisyui.com/components)
|
||||
|
||||
|
||||
<!--
|
||||
Configuration auto-generated by `langgraph template lock`. DO NOT EDIT MANUALLY.
|
||||
{
|
||||
"config_schemas": {
|
||||
"agent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"model": {
|
||||
"type": "string",
|
||||
"default": "anthropic/claude-3-5-sonnet-20240620",
|
||||
"description": "The name of the language model to use for the agent's main interactions. Should be in the form: provider/model-name.",
|
||||
"environment": [
|
||||
{
|
||||
"value": "anthropic/claude-1.2",
|
||||
"variables": "ANTHROPIC_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "anthropic/claude-2.0",
|
||||
"variables": "ANTHROPIC_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "anthropic/claude-2.1",
|
||||
"variables": "ANTHROPIC_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "anthropic/claude-3-5-sonnet-20240620",
|
||||
"variables": "ANTHROPIC_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "anthropic/claude-3-haiku-20240307",
|
||||
"variables": "ANTHROPIC_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "anthropic/claude-3-opus-20240229",
|
||||
"variables": "ANTHROPIC_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "anthropic/claude-3-sonnet-20240229",
|
||||
"variables": "ANTHROPIC_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "anthropic/claude-instant-1.2",
|
||||
"variables": "ANTHROPIC_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-3.5-turbo",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-3.5-turbo-0125",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-3.5-turbo-0301",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-3.5-turbo-0613",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-3.5-turbo-1106",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-3.5-turbo-16k",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-3.5-turbo-16k-0613",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-0125-preview",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-0314",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-0613",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-1106-preview",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-32k",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-32k-0314",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-32k-0613",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-turbo",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-turbo-preview",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4-vision-preview",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4o",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
},
|
||||
{
|
||||
"value": "openai/gpt-4o-mini",
|
||||
"variables": "OPENAI_API_KEY"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"environment": [
|
||||
"TAVILY_API_KEY"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
-->
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"dependencies": ["."],
|
||||
"graphs": {
|
||||
"agent": "./src/react_agent/graph.py:graph"
|
||||
},
|
||||
"env": ".env",
|
||||
"http": {
|
||||
"app": "./src/react_agent/app.py:app"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
[project]
|
||||
name = "react-agent"
|
||||
version = "0.0.1"
|
||||
description = "Starter template for making a custom Reasoning and Action agent (using tool calling) in LangGraph."
|
||||
authors = [
|
||||
{ name = "William Fu-Hinthorn", email = "13333726+hinthornw@users.noreply.github.com" },
|
||||
]
|
||||
readme = "README.md"
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"langgraph>=0.2.6",
|
||||
"langchain-openai>=0.1.22",
|
||||
"langchain-anthropic>=0.1.23",
|
||||
"langchain>=0.3.19",
|
||||
"langchain-fireworks>=0.1.7",
|
||||
"python-dotenv>=1.0.1",
|
||||
"langchain-community>=0.2.17",
|
||||
"tavily-python>=0.4.0",
|
||||
"python-fasthtml>=0.12.1",
|
||||
"langgraph-sdk>=0.1.51",
|
||||
]
|
||||
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["mypy>=1.11.1", "ruff>=0.6.1"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["langgraph.templates.react_agent", "react_agent"]
|
||||
[tool.setuptools.package-dir]
|
||||
"langgraph.templates.react_agent" = "src/react_agent"
|
||||
"react_agent" = "src/react_agent"
|
||||
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"*" = ["py.typed"]
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [
|
||||
"E", # pycodestyle
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"D", # pydocstyle
|
||||
"D401", # First line should be in imperative mood
|
||||
"T201",
|
||||
"UP",
|
||||
]
|
||||
lint.ignore = [
|
||||
"UP006",
|
||||
"UP007",
|
||||
# We actually do want to import from typing_extensions
|
||||
"UP035",
|
||||
# Relax the convention by _not_ requiring documentation for every function parameter.
|
||||
"D417",
|
||||
"E501",
|
||||
]
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/*" = ["D", "UP"]
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"langgraph-cli[inmem]>=0.1.73",
|
||||
"pytest>=8.3.4",
|
||||
]
|
||||
@@ -0,0 +1,9 @@
|
||||
"""React Agent.
|
||||
|
||||
This module defines a custom reasoning and action agent graph.
|
||||
It invokes tools in a simple loop.
|
||||
"""
|
||||
|
||||
from react_agent.graph import graph
|
||||
|
||||
__all__ = ["graph"]
|
||||
@@ -0,0 +1,83 @@
|
||||
from fasthtml.common import (
|
||||
Button,
|
||||
Div,
|
||||
FastHTML,
|
||||
Form,
|
||||
Group,
|
||||
Hidden,
|
||||
Input,
|
||||
Link,
|
||||
Script,
|
||||
Titled,
|
||||
picolink,
|
||||
)
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
# None means point to the local run loop
|
||||
langgraph_client = get_client()
|
||||
|
||||
# Set up the app, including daisyui and tailwind for the chat component
|
||||
hdrs = (
|
||||
picolink,
|
||||
Script(src="https://cdn.tailwindcss.com"),
|
||||
Link(
|
||||
rel="stylesheet",
|
||||
href="https://cdn.jsdelivr.net/npm/daisyui@4.11.1/dist/full.min.css",
|
||||
),
|
||||
)
|
||||
app = FastHTML(hdrs=hdrs, cls="p-4 max-w-lg mx-auto")
|
||||
|
||||
|
||||
# Chat message component (renders a chat bubble)
|
||||
def ChatMessage(msg, user):
|
||||
bubble_class = "chat-bubble-primary" if user else "chat-bubble-secondary"
|
||||
chat_class = "chat-end" if user else "chat-start"
|
||||
return Div(cls=f"chat {chat_class}")(
|
||||
Div("user" if user else "assistant", cls="chat-header"),
|
||||
Div(msg, cls=f"chat-bubble {bubble_class}"),
|
||||
Hidden(msg, name="messages"),
|
||||
)
|
||||
|
||||
|
||||
# The input field for the user message. Also used to clear the
|
||||
# input field after sending a message via an OOB swap
|
||||
def ChatInput():
|
||||
return Input(
|
||||
name="msg",
|
||||
id="msg-input",
|
||||
placeholder="Type a message",
|
||||
cls="input input-bordered w-full",
|
||||
hx_swap_oob="true",
|
||||
)
|
||||
|
||||
|
||||
# The main screen
|
||||
@app.get("/")
|
||||
def index():
|
||||
page = Form(hx_post=send, hx_target="#chatlist", hx_swap="beforeend")(
|
||||
Div(id="chatlist", cls="chat-box h-[73vh] overflow-y-auto"),
|
||||
Div(cls="flex space-x-2 mt-2")(
|
||||
Group(ChatInput(), Button("Send", cls="btn btn-primary"))
|
||||
),
|
||||
)
|
||||
return Titled("Chatbot Demo", page)
|
||||
|
||||
|
||||
# Handle the form submission
|
||||
@app.post("/send")
|
||||
async def send(msg: str, messages: list[str] = None):
|
||||
if not messages:
|
||||
messages = []
|
||||
messages.append(msg.rstrip())
|
||||
result = await langgraph_client.runs.wait(
|
||||
None,
|
||||
"agent",
|
||||
input={"messages": messages},
|
||||
)
|
||||
response = result["messages"][-1]["content"]
|
||||
|
||||
return (
|
||||
ChatMessage(msg, True), # The user's message
|
||||
ChatMessage(response.strip(), False), # The chatbot's response
|
||||
ChatInput(),
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langgraph.prebuilt import create_react_agent
|
||||
|
||||
PROMPT = "You are a friendly, curious, geeky AI."
|
||||
|
||||
# Define the function that calls the model
|
||||
|
||||
|
||||
def prompt(state, config: RunnableConfig):
|
||||
"""Prepare messages for the model."""
|
||||
sys_prompt = config["configurable"].get("system_prompt")
|
||||
sys_prompt = sys_prompt or PROMPT
|
||||
return [("system", sys_prompt), *state["messages"]]
|
||||
|
||||
|
||||
llm = init_chat_model("anthropic:claude-3-5-sonnet-latest")
|
||||
graph = create_react_agent(llm, tools=[], prompt=prompt)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
"""Define any integration tests you want in this directory."""
|
||||
@@ -0,0 +1,12 @@
|
||||
import pytest
|
||||
|
||||
from react_agent import graph
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.langsmith
|
||||
async def test_react_agent_simple_passthrough() -> None:
|
||||
# Add your own tests here.
|
||||
await graph.ainvoke(
|
||||
{"messages": [("user", "Hi there!")]},
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
"""Define any unit tests you may want in this directory."""
|
||||
@@ -0,0 +1,3 @@
|
||||
def test_graph() -> None:
|
||||
# Add your own tests here.
|
||||
pass
|
||||
Reference in New Issue
Block a user