mirror of
https://github.com/langchain-ai/reply_gAI.git
synced 2026-07-01 08:35:18 -04:00
Dir rename
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"dockerfile_lines": [],
|
||||
"graphs": {
|
||||
"reply_gai": "./src/agent/graph.py:graph"
|
||||
"reply_gai": "./src/chatbot/graph.py:graph"
|
||||
},
|
||||
"python_version": "3.11",
|
||||
"env": "./.env",
|
||||
|
||||
+2
-2
@@ -23,10 +23,10 @@ requires = ["setuptools>=73.0.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools]
|
||||
packages = ["agent"]
|
||||
packages = ["chatbot"]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"agent" = "src/agent"
|
||||
"chatbot" = "src/chatbot"
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"*" = ["py.typed"]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,8 @@
|
||||
import os
|
||||
from dataclasses import dataclass, field, fields
|
||||
from dataclasses import dataclass, fields
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from typing_extensions import Annotated
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
@@ -9,9 +9,9 @@ from langgraph.store.base import BaseStore
|
||||
from langgraph.graph import END, StateGraph
|
||||
from arcadepy import Arcade
|
||||
|
||||
import agent.configuration as configuration
|
||||
from agent.prompts import CHAT_INSTRUCTIONS
|
||||
from agent.utils import get_all_tweets
|
||||
import chatbot.configuration as configuration
|
||||
from chatbot.prompts import CHAT_INSTRUCTIONS
|
||||
from chatbot.utils import get_all_tweets
|
||||
|
||||
def get_tweets(state: MessagesState, config: RunnableConfig, store: BaseStore) -> dict:
|
||||
"""Fetch and store recent tweets for a specified Twitter user.
|
||||
@@ -1,4 +1,6 @@
|
||||
CHAT_INSTRUCTIONS = """Hey! You're chatting as @{username} on Twitter. I've pulled some of their recent tweets to help you get their vibe:
|
||||
CHAT_INSTRUCTIONS = """Hey! You're chatting as @{username} on Twitter.
|
||||
|
||||
I've pulled some of their recent tweets to help you get their vibe:
|
||||
|
||||
{tweets}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%capture --no-stderr\n",
|
||||
"%pip install --quiet -U langgraph langchain_community langchain_core arcade_x"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 29,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import uuid\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime, timezone\n",
|
||||
"from langchain_core.runnables import RunnableConfig\n",
|
||||
"from langchain_core.messages import SystemMessage\n",
|
||||
"from langchain_anthropic import ChatAnthropic \n",
|
||||
"from langgraph.graph import MessagesState\n",
|
||||
"from langgraph.store.base import BaseStore\n",
|
||||
"from langgraph.graph import END, StateGraph\n",
|
||||
"from arcadepy import Arcade\n",
|
||||
"\n",
|
||||
"import agent.configuration as configuration\n",
|
||||
"from agent.prompts import CHAT_INSTRUCTIONS\n",
|
||||
"from agent.utils import get_all_tweets\n",
|
||||
"\n",
|
||||
"def get_tweets(state: MessagesState, config: RunnableConfig, store: BaseStore) -> dict:\n",
|
||||
" \"\"\"Fetch and store recent tweets for a specified Twitter user.\n",
|
||||
" \n",
|
||||
" This function authenticates with the Arcade API, retrieves recent tweets for a given\n",
|
||||
" username, and stores them in the provided BaseStore instance. Each tweet is stored\n",
|
||||
" with its text content and URL.\n",
|
||||
" \n",
|
||||
" Args:\n",
|
||||
" state (MessagesState): Current conversation state (unused but required by graph)\n",
|
||||
" config (RunnableConfig): Configuration object containing settings like username\n",
|
||||
" store (BaseStore): Storage interface for saving retrieved tweets\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" dict: Empty dictionary (function stores tweets but doesn't return them)\n",
|
||||
" \n",
|
||||
" Note:\n",
|
||||
" - Requires ARCADE_USER_ID environment variable to be set\n",
|
||||
" - Fetches up to 100 most recent tweets from the last 7 days\n",
|
||||
" - Stores tweets using (username, \"tweets\") as namespace\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" # Get the configuration\n",
|
||||
" configurable = configuration.Configuration.from_runnable_config(config)\n",
|
||||
"\n",
|
||||
" client = Arcade() \n",
|
||||
" USER_ID = os.environ[\"ARCADE_USER_ID\"]\n",
|
||||
" TOOL_NAME = \"X.SearchRecentTweetsByUsername\"\n",
|
||||
"\n",
|
||||
" auth_response = client.tools.authorize(\n",
|
||||
" tool_name=TOOL_NAME,\n",
|
||||
" user_id=USER_ID,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" if auth_response.status != \"completed\":\n",
|
||||
" print(f\"Click this link to authorize: {auth_response.authorization_url}\")\n",
|
||||
"\n",
|
||||
" # Wait for the authorization to complete\n",
|
||||
" client.auth.wait_for_completion(auth_response)\n",
|
||||
"\n",
|
||||
" # Search for recent tweets (last 7 days) on X (Twitter)\n",
|
||||
" username = configurable.username\n",
|
||||
"\n",
|
||||
" # Get all the tweets\n",
|
||||
" tweets = get_all_tweets(client, username, USER_ID, TOOL_NAME)\n",
|
||||
"\n",
|
||||
" # Load the tweets into memory\n",
|
||||
" namespace_for_memory = (username, \"tweets\")\n",
|
||||
" for tweet in tweets:\n",
|
||||
" memory_id = tweet.get('id',uuid.uuid4())\n",
|
||||
" text = tweet.get('text',\"Tweet empty\")\n",
|
||||
" url = tweet.get('tweet_url',\"URL not found\")\n",
|
||||
" store.put(namespace_for_memory, memory_id, {\"text\": text,\"url\": url})\n",
|
||||
"\n",
|
||||
"def chat(state: MessagesState, config: RunnableConfig, store: BaseStore) -> dict:\n",
|
||||
" \"\"\"Generate a chat response in the style of a specific Twitter user.\n",
|
||||
" \n",
|
||||
" This function retrieves tweets from the store for a given username, formats them,\n",
|
||||
" and uses them as context for Claude to generate a response that mimics the user's\n",
|
||||
" writing style and personality.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" state (MessagesState): Current conversation state containing message history\n",
|
||||
" config (RunnableConfig): Configuration object containing settings like username\n",
|
||||
" store (BaseStore): Storage interface for accessing saved tweets\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" dict: Contains the generated message in the 'messages' key\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" # Get the configuration\n",
|
||||
" configurable = configuration.Configuration.from_runnable_config(config)\n",
|
||||
" username = configurable.username\n",
|
||||
" \n",
|
||||
" # Get the tweets\n",
|
||||
" namespace_for_memory = (username, \"tweets\")\n",
|
||||
"\n",
|
||||
" # Get all the tweets\n",
|
||||
" memories = []\n",
|
||||
" while mems := store.search(namespace_for_memory, limit=200, offset=len(memories)):\n",
|
||||
" memories.extend(mems)\n",
|
||||
" \n",
|
||||
" # Format the tweets\n",
|
||||
" formatted_output = \"\"\n",
|
||||
" for memory in memories:\n",
|
||||
" tweet = memory.value\n",
|
||||
" formatted_output += f\"@{username}: {tweet['text']}\\n\"\n",
|
||||
" formatted_output += \"-\" * 80 + \"\\n\"\n",
|
||||
"\n",
|
||||
" # Generate a response\n",
|
||||
" claude_3_5_sonnet = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\", temperature=0.75) \n",
|
||||
" chat_instructions_formatted = CHAT_INSTRUCTIONS.format(username=username,tweets=formatted_output)\n",
|
||||
" msg = claude_3_5_sonnet.invoke([SystemMessage(content=chat_instructions_formatted)]+state['messages'])\n",
|
||||
" return {\"messages\": [msg]} \n",
|
||||
"\n",
|
||||
"def route_to_tweet_loader(state: MessagesState, config: RunnableConfig, store: BaseStore) -> dict:\n",
|
||||
" \"\"\"Route the workflow based on tweet availability and age.\n",
|
||||
" \n",
|
||||
" This function determines whether to fetch new tweets or proceed to chat by checking:\n",
|
||||
" 1. If tweets exist for the user in the store\n",
|
||||
" 2. If existing tweets are too old (beyond max_tweet_age_seconds)\n",
|
||||
" \n",
|
||||
" Args:\n",
|
||||
" state (MessagesState): Current conversation state\n",
|
||||
" config (RunnableConfig): Configuration containing username and tweet age settings\n",
|
||||
" store (BaseStore): Storage interface for accessing saved tweets\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" str: Either \"get_tweets\" to fetch new tweets or \"chat\" to proceed with conversation\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" # Get the configuration\n",
|
||||
" configurable = configuration.Configuration.from_runnable_config(config)\n",
|
||||
" username = configurable.username\n",
|
||||
" \n",
|
||||
" # If we have Tweets from the user, go to chat\n",
|
||||
" namespace_for_memory = (username, \"tweets\")\n",
|
||||
" memories = store.search(namespace_for_memory, limit=200)\n",
|
||||
"\n",
|
||||
" # If we have tweets, check if they're too old\n",
|
||||
" if memories: \n",
|
||||
" # Get most recent tweet timestamp\n",
|
||||
" most_recent = max(mem.created_at for mem in memories)\n",
|
||||
" \n",
|
||||
" # Calculate time difference\n",
|
||||
" now = datetime.now(timezone.utc)\n",
|
||||
" time_delta = now - most_recent\n",
|
||||
" \n",
|
||||
" # If tweets are too old, get new ones\n",
|
||||
" if time_delta.total_seconds() > configurable.max_tweet_age_seconds:\n",
|
||||
" return \"get_tweets\"\n",
|
||||
" return \"chat\"\n",
|
||||
" # If no tweets for the user, get them \n",
|
||||
" else:\n",
|
||||
" return \"get_tweets\"\n",
|
||||
"\n",
|
||||
"# Create the graph + all nodes\n",
|
||||
"builder = StateGraph(MessagesState, config_schema=configuration.Configuration)\n",
|
||||
"builder.add_node(\"chat\",chat)\n",
|
||||
"builder.add_node(\"get_tweets\",get_tweets)\n",
|
||||
"builder.set_conditional_entry_point(route_to_tweet_loader, [\"chat\", \"get_tweets\"])\n",
|
||||
"builder.add_edge(\"get_tweets\", \"chat\")\n",
|
||||
"builder.add_edge(\"chat\", END)\n",
|
||||
"\n",
|
||||
"# Store\n",
|
||||
"from langgraph.store.memory import InMemoryStore\n",
|
||||
"in_memory_store = InMemoryStore()\n",
|
||||
"\n",
|
||||
"# Compile the graph\n",
|
||||
"graph = builder.compile(store=in_memory_store)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'messages': [HumanMessage(content='What are some of your favorite applications of LLMs?', additional_kwargs={}, response_metadata={}, id='ca0873c2-fe1e-4d67-a106-46458c8c0820'),\n",
|
||||
" AIMessage(content=\"Oh man, I love this question! LLMs are just mind-blowing in terms of their potential applications. One of my absolute favorites, which I've been geeking out about recently, is using LLMs as reading companions for books. \\n\\nImagine you're diving into a complex non-fiction book or a dense sci-fi novel, and you've got this AI sidekick that's read the whole thing, understands the context, and can chat with you about it in real-time. You could ask questions, get summaries, or even have generated discussions about themes and ideas. It's like NotebookLM on steroids!\\n\\nI actually tweeted about this recently - I think if Amazon built something like this into Kindle, it would be absolutely game-changing. They've got all the content right there, after all.\\n\\nBeyond that, I'm really excited about LLMs in coding assistants. The potential for accelerating software development is huge. And of course, there's the whole world of creative applications - writing, brainstorming, even aiding in scientific research and hypothesis generation.\\n\\nBut honestly, we're just scratching the surface. The most mind-blowing applications of LLMs are probably things we haven't even thought of yet. It's like we've discovered fire and we're still figuring out all the ways we can use it. Exciting times!\", additional_kwargs={}, response_metadata={'id': 'msg_01Xpv4yY4jLc3EEBpgk52SfR', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 1128, 'output_tokens': 299}}, id='run-c6abe3f1-7cf4-4f55-a6fc-270a6186693a-0', usage_metadata={'input_tokens': 1128, 'output_tokens': 299, 'total_tokens': 1427, 'input_token_details': {}})]}"
|
||||
]
|
||||
},
|
||||
"execution_count": 30,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from langchain_core.messages import HumanMessage\n",
|
||||
"config = {\"configurable\": {\"username\": \"karpathy\"}}\n",
|
||||
"graph.invoke({\"messages\": [HumanMessage(content=\"What are some of your favorite applications of LLMs?\")]}, config=config)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"14\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "reply_gai_env",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
Reference in New Issue
Block a user