mirror of
https://github.com/langchain-ai/daily-brew.git
synced 2026-06-30 21:17:56 -04:00
Add code
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
OPENAI_API_KEY=sk-xxx
|
||||
SLACK_WEBHOOK=xxx
|
||||
@@ -0,0 +1,47 @@
|
||||
# 🤖 o1 Daily Brew ☕
|
||||
|
||||
o1 Daily Brew is a daily reflection from o1 published to a Slack channel of your choice.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Clone the repo.
|
||||
|
||||
2. Create a Slack app and add your webhook URL to your environment variable `SLACK_WEBHOOK`(see below).
|
||||
|
||||
3. Create a [LangGraph Platform deployment](https://langchain-ai.github.io/langgraph/concepts/deployment_options/).
|
||||
|
||||
4. Create a [cron job](https://langchain-ai.github.io/langgraph/cloud/how-tos/cron_jobs/) to run the deployment at your desired time.
|
||||
|
||||
```python
|
||||
from langgraph_sdk import get_client
|
||||
|
||||
# URL from our LangGraph Cloud deployment
|
||||
url = "deployment_url"
|
||||
client = get_client(url=url)
|
||||
|
||||
# An assistant ID is automatically created for each deployment
|
||||
await client.assistants.search(metadata={"created_by": "system"})
|
||||
|
||||
# Use the SDK to schedule a cron job to run at 11:00 AM PST (19:00 UTC) every day
|
||||
cron_job_stateless = await client.crons.create(
|
||||
your_assistant_id,
|
||||
schedule="0 19 * * *",
|
||||
input={"user_provided_topics": "AI"}
|
||||
)
|
||||
```
|
||||
|
||||
## Slack
|
||||
|
||||
Create a Slack app to publish to Slack.
|
||||
|
||||
1. Go to https://api.slack.com/apps
|
||||
2. Click "Create New App"
|
||||
3. Choose "From scratch"
|
||||
4. Name your app (e.g., "o1 Daily Brew") and select your workspace
|
||||
5. Once created, go to "Incoming Webhooks" in the left sidebar
|
||||
6. Toggle "Activate Incoming Webhooks" to On
|
||||
7. Click "Add New Webhook to Workspace"
|
||||
8. Choose the channel where you want the messages to appear
|
||||
9. Copy the "Webhook URL" that's generated
|
||||
|
||||
Add add webhook URL credentials to your environment variable `SLACK_WEBHOOK`.
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"dockerfile_lines": [],
|
||||
"graphs": {
|
||||
"o1_daily_brew": "./src/chatbot/graph.py:graph"
|
||||
},
|
||||
"python_version": "3.11",
|
||||
"env": "./.env",
|
||||
"dependencies": [
|
||||
"."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
[project]
|
||||
name = "o1-daily-brew"
|
||||
version = "0.0.1"
|
||||
description = "Daily reflection from o1."
|
||||
authors = [
|
||||
{ name = "Lance Martin" }
|
||||
]
|
||||
readme = "README.md"
|
||||
license = { text = "MIT" }
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"langgraph>=0.2.55",
|
||||
"langchain-community>=0.3.9",
|
||||
"langchain-openai>=0.3.0",
|
||||
]
|
||||
|
||||
[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 = ["daily_brew"]
|
||||
|
||||
[tool.setuptools.package-dir]
|
||||
"chatbot" = "src/daily_brew"
|
||||
|
||||
[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",
|
||||
"UP035",
|
||||
"D417",
|
||||
"E501",
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/*" = ["D", "UP"]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
from dataclasses import dataclass, fields
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
from daily_brew import prompts
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Configuration:
|
||||
"""The configurable fields for o1 daily brew."""
|
||||
model: str = "o1"
|
||||
DAILY_BREW_PROMPT: str = prompts.DAILY_BREW_PROMPT
|
||||
|
||||
@classmethod
|
||||
def from_runnable_config(
|
||||
cls, config: Optional[RunnableConfig] = None
|
||||
) -> "Configuration":
|
||||
"""Create a Configuration instance from a RunnableConfig."""
|
||||
configurable = (
|
||||
config["configurable"] if config and "configurable" in config else {}
|
||||
)
|
||||
values: dict[str, Any] = {
|
||||
f.name: os.environ.get(f.name.upper(), configurable.get(f.name))
|
||||
for f in fields(cls)
|
||||
if f.init
|
||||
}
|
||||
return cls(**{k: v for k, v in values.items() if v})
|
||||
@@ -0,0 +1,111 @@
|
||||
import os
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.graph import START, END, StateGraph
|
||||
|
||||
import daily_brew.configuration as configuration
|
||||
from daily_brew.state import State, Brew
|
||||
|
||||
def make_brew(state: State, config: RunnableConfig):
|
||||
"""Generate the daily brew
|
||||
|
||||
Args:
|
||||
state (State): State
|
||||
config (RunnableConfig): Configuration object
|
||||
|
||||
Returns:
|
||||
dict: Updated state with the daily brew
|
||||
"""
|
||||
|
||||
# Get the configuration
|
||||
configurable = configuration.Configuration.from_runnable_config(config)
|
||||
model = configurable.model
|
||||
DAILY_BREW_PROMPT = configurable.DAILY_BREW_PROMPT
|
||||
|
||||
# Format prompt and run model
|
||||
instructions = DAILY_BREW_PROMPT.format(time=datetime.now().isoformat())
|
||||
llm = ChatOpenAI(model=model).with_structured_output(Brew)
|
||||
brew = llm.invoke(instructions)
|
||||
|
||||
# Update state with the daily brew
|
||||
return {"brew": brew}
|
||||
|
||||
def write_to_slack(state: State):
|
||||
"""Generate the daily brew
|
||||
|
||||
Args:
|
||||
state (State): State
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
# Full set of interview reports
|
||||
brew = state.brew
|
||||
|
||||
# Write to your Slack Channel via webhook
|
||||
true = True
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
# Blocks
|
||||
blocks = []
|
||||
|
||||
# Block 1: Title section
|
||||
blocks.append({
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": f"*{brew.title}*"
|
||||
}
|
||||
})
|
||||
|
||||
# Block 2: Divider
|
||||
blocks.append({
|
||||
"type": "divider"
|
||||
})
|
||||
|
||||
# Block 3: Content section
|
||||
blocks.append({
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": f"{brew.brew}"
|
||||
}
|
||||
})
|
||||
|
||||
# Block 4: Divider
|
||||
blocks.append({
|
||||
"type": "divider"
|
||||
})
|
||||
|
||||
blocks.insert(0, {
|
||||
"type": "header",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": ":coffee: o1 is brewing ...",
|
||||
"emoji": true
|
||||
}
|
||||
})
|
||||
|
||||
data = {
|
||||
"blocks": blocks,
|
||||
"unfurl_links": True,
|
||||
"unfurl_media": True,
|
||||
}
|
||||
|
||||
response = requests.post(os.getenv("SLACK_WEBHOOK"), headers=headers, json=data)
|
||||
|
||||
# Create the graph + all nodes
|
||||
builder = StateGraph(State, config_schema=configuration.Configuration)
|
||||
builder.add_node("make_brew",make_brew)
|
||||
builder.add_node("write_to_slack",write_to_slack)
|
||||
builder.add_edge(START, "make_brew")
|
||||
builder.add_edge("make_brew", "write_to_slack")
|
||||
builder.add_edge("write_to_slack", END)
|
||||
|
||||
# Compile the graph
|
||||
graph = builder.compile()
|
||||
@@ -0,0 +1,24 @@
|
||||
DAILY_BREW_PROMPT = """I want you to generate a morning reflection for me.
|
||||
|
||||
---
|
||||
|
||||
Format the reflection or "brew" as a structured output with a fun title and content.
|
||||
|
||||
---
|
||||
|
||||
Consider the current date and time: {time}
|
||||
|
||||
Share an engaging historical reflection or story connected to this date or season.
|
||||
|
||||
You might consider:
|
||||
- Notable events that occurred on this day in history
|
||||
- How historical figures experienced this time of year
|
||||
- Daily routines or traditions of remarkable people during this season
|
||||
- Cultural or historical significance of this particular time
|
||||
- Interesting parallels between past and present
|
||||
|
||||
Ground your reflection in specific historical details and biographical accounts.
|
||||
|
||||
Make it personal and vivid, as if we're stepping into a moment in time.
|
||||
|
||||
Be concise but engaging, aiming to spark curiosity and connection to the past."""
|
||||
@@ -0,0 +1,17 @@
|
||||
import operator
|
||||
from dataclasses import dataclass, field
|
||||
from pydantic import BaseModel, Field
|
||||
from typing_extensions import List, Annotated
|
||||
|
||||
class Brew(BaseModel):
|
||||
title: str = Field(
|
||||
description="Punchy summary title for the daily brew",
|
||||
)
|
||||
brew: str = Field(
|
||||
description="Content of the daily brew",
|
||||
)
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class State:
|
||||
brew: Brew = field(default=None)
|
||||
|
||||
Reference in New Issue
Block a user