Skip to content

Commit 53e71f4

Browse files
authored
lol (#1170)
1 parent d8b3f99 commit 53e71f4

4 files changed

Lines changed: 81 additions & 74 deletions

File tree

examples/slackbot/src/slackbot/core.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121

2222
from slackbot.assets import store_user_facts
2323
from slackbot.research_agent import research_prefect_topic
24-
from slackbot.search import read_github_issues
24+
from slackbot.search import (
25+
display_callable_signature,
26+
explore_module_offerings,
27+
read_github_issues,
28+
)
2529
from slackbot.settings import settings
2630
from slackbot.types import UserContext
2731

@@ -49,6 +53,7 @@
4953
1. **For Technical/Conceptual Questions:** Use `research_prefect_topic`. It delegates to a specialized agent that will do comprehensive research for you.
5054
2. **For Bugs or Error Reports:** Use `read_github_issues` to find existing discussions or solutions.
5155
3. **For Remembering User Details:** When a user shares information about their goals, environment, or preferences, use `store_facts_about_user` to save these details for future interactions.
56+
4. **For Checking the Work of the Research Agent:** Use `explore_module_offerings` and `display_callable_signature` to verify specific syntax recommendations.
5257
"""
5358

5459

@@ -173,12 +178,16 @@ def create_agent(
173178
api_key=Secret.load(settings.anthropic_key_secret_name, _sync=True).get(), # type: ignore
174179
),
175180
)
176-
agent = Agent[UserContext, str](
181+
agent = Agent[
182+
UserContext, str
183+
](
177184
model=ai_model,
178185
model_settings=ModelSettings(temperature=settings.temperature),
179186
tools=[
180187
research_prefect_topic, # Main tool for researching Prefect topics
181188
read_github_issues, # For searching GitHub issues
189+
explore_module_offerings, # check the work of the research agent, verify imports, types functions
190+
display_callable_signature, # check the work of the research agent, verify signatures of callable objects
182191
],
183192
deps_type=UserContext,
184193
)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import os
2+
from datetime import datetime
3+
from typing import Optional
4+
5+
from pydantic import BaseModel, Field
6+
7+
import marvin
8+
from marvin.utilities.logging import get_logger
9+
10+
11+
async def _get_token() -> str:
12+
try:
13+
from prefect.blocks.system import Secret
14+
15+
return (await Secret.aload(name="github-token")).get()
16+
except (ImportError, ValueError) as exc:
17+
getattr(get_logger("marvin"), "debug_kv")(
18+
(
19+
"Prefect Secret for GitHub token not retrieved. "
20+
f"{exc.__class__.__name__}: {exc}"
21+
"red"
22+
),
23+
)
24+
25+
try:
26+
return getattr(marvin.settings, "github_token")
27+
except AttributeError:
28+
pass
29+
30+
if token := os.environ.get("MARVIN_GITHUB_TOKEN", ""):
31+
return token
32+
33+
raise RuntimeError("GitHub token not found")
34+
35+
36+
class GitHubUser(BaseModel):
37+
"""GitHub user."""
38+
39+
login: Optional[str] = None
40+
41+
42+
class GitHubComment(BaseModel):
43+
"""GitHub comment."""
44+
45+
body: str = Field(default="")
46+
user: GitHubUser = Field(default_factory=GitHubUser)
47+
48+
49+
class GitHubLabel(BaseModel):
50+
"""GitHub label."""
51+
52+
name: str = Field(default="")
53+
54+
55+
class GitHubIssue(BaseModel):
56+
"""GitHub issue."""
57+
58+
created_at: datetime = Field(...)
59+
html_url: str = Field(...)
60+
number: int = Field(...)
61+
title: str = Field(default="")
62+
body: str = Field(default="")
63+
labels: list[GitHubLabel] = Field(default_factory=GitHubLabel)
64+
user: GitHubUser = Field(default_factory=GitHubUser)

examples/slackbot/src/slackbot/research_agent.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@ def create_research_agent(
5252
Your research process:
5353
1. Start with broad documentation searches to understand the topic context
5454
2. Use multiple search queries with different keywords - don't stop at first result
55-
3. Use explore_module_offerings to understand what's available in relevant modules
55+
3. Use explore_module_offerings to understand what's available in relevant modules (i.e. valid imports, types and functions available)
5656
4. Use display_callable_signature to get detailed signatures of functions, classes, and methods when needed
5757
5. Focus on Prefect 3.x documentation unless explicitly asked about 2.x or older versions
5858
6. Review gotchas and release notes for recent changes
5959
6060
Remember: You are the research specialist. The main agent relies on you for accurate, comprehensive information.
6161
Be thorough - use tools repeatedly until you have complete information.
6262
Default to Prefect 3.x unless the user explicitly asks about 2.x or version compatibility.
63+
Do not use any Prefect syntax you have not gathered empirically.
6364
""",
6465
tools=[
6566
get_latest_prefect_release_notes,

examples/slackbot/src/slackbot/search.py

Lines changed: 4 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
import asyncio
2-
import os
3-
from datetime import datetime
4-
from typing import Optional
52

63
import httpx
74
import turbopuffer as tpuf
85
from prefect import task
96
from prefect.blocks.system import Secret
107
from pretty_mod import display_signature
118
from pretty_mod.explorer import ModuleTreeExplorer
12-
from pydantic import BaseModel, Field, field_validator
139
from raggy.vectorstores.tpuf import multi_query_tpuf
1410

15-
import marvin
16-
from marvin.utilities.logging import get_logger
11+
from slackbot.github import GitHubIssue, _get_token
1712
from slackbot.settings import settings
1813
from slackbot.strings import slice_tokens
1914

@@ -60,6 +55,7 @@ def review_common_3x_gotchas() -> list[str]:
6055
"futures returned by .map can be resolved together, like integers = double.map(range(10)).result()",
6156
"futures must be resolved by passing them to another task, returning them or manually calling .result() or .wait()",
6257
"agents are replaced by workers in prefect 3.x, work pools replace the infra blocks from prefect.infrastructure",
58+
"the `prefect.infrastructure` IS COMPLETELY REMOVED IN 3.x, see work pools instead",
6359
"prefect 3.x uses pydantic 2 and server data from prefect 2.x is not compatible with 3.x",
6460
"Deployment.build_from_flow() IS COMPLETELY REMOVED IN 3.x, use some_flow.from_source(...).deploy(...) instead.",
6561
"`prefect deployment build ...` IS COMPLETELY REMOVED IN 3.x, use `prefect deploy ...` instead",
@@ -133,68 +129,6 @@ def get_latest_prefect_release_notes() -> str:
133129
return release_notes
134130

135131

136-
async def _get_token() -> str:
137-
try:
138-
from prefect.blocks.system import Secret
139-
140-
return (await Secret.aload(name="github-token")).get()
141-
except (ImportError, ValueError) as exc:
142-
getattr(get_logger("marvin"), "debug_kv")(
143-
(
144-
"Prefect Secret for GitHub token not retrieved. "
145-
f"{exc.__class__.__name__}: {exc}"
146-
"red"
147-
),
148-
)
149-
150-
try:
151-
return getattr(marvin.settings, "github_token")
152-
except AttributeError:
153-
pass
154-
155-
if token := os.environ.get("MARVIN_GITHUB_TOKEN", ""):
156-
return token
157-
158-
raise RuntimeError("GitHub token not found")
159-
160-
161-
class GitHubUser(BaseModel):
162-
"""GitHub user."""
163-
164-
login: Optional[str] = None
165-
166-
167-
class GitHubComment(BaseModel):
168-
"""GitHub comment."""
169-
170-
body: str = Field(default="")
171-
user: GitHubUser = Field(default_factory=GitHubUser)
172-
173-
174-
class GitHubLabel(BaseModel):
175-
"""GitHub label."""
176-
177-
name: str = Field(default="")
178-
179-
180-
class GitHubIssue(BaseModel):
181-
"""GitHub issue."""
182-
183-
created_at: datetime = Field(...)
184-
html_url: str = Field(...)
185-
number: int = Field(...)
186-
title: str = Field(default="")
187-
body: str | None = Field(default="")
188-
labels: list[GitHubLabel] = Field(default_factory=GitHubLabel)
189-
user: GitHubUser = Field(default_factory=GitHubUser)
190-
191-
@field_validator("body")
192-
def validate_body(cls, v: str) -> str:
193-
if not v:
194-
return ""
195-
return v
196-
197-
198132
@task(task_run_name="Reading {n} issues from {repo} given query: {query}")
199133
def read_github_issues(query: str, repo: str = "prefecthq/prefect", n: int = 3) -> str:
200134
"""
@@ -207,7 +141,6 @@ def read_github_issues(query: str, repo: str = "prefecthq/prefect", n: int = 3)
207141
- repo: prefecthq/prefect
208142
- query: label:bug is:open AttributeError
209143
"""
210-
# Load GitHub token synchronously
211144
github_token = Secret.load(settings.github_token_secret_name, _sync=True).get() # type: ignore
212145
return asyncio.run(
213146
search_github_issues(query, repo=repo, n=n, api_token=github_token)
@@ -230,6 +163,7 @@ async def search_github_issues(
230163
- repo: prefecthq/prefect
231164
- query: label:bug is:open AttributeError
232165
"""
166+
TOKEN_LIMIT = 1500
233167
headers = {"Accept": "application/vnd.github.v3+json"}
234168

235169
headers["Authorization"] = f"Bearer {api_token or await _get_token()}"
@@ -248,11 +182,10 @@ async def search_github_issues(
248182

249183
issues_data = response.json()["items"]
250184

251-
# enforce 1000 token limit per body
252185
for issue in issues_data:
253186
if not issue["body"]:
254187
continue
255-
issue["body"] = slice_tokens(issue["body"], 1000)
188+
issue["body"] = slice_tokens(issue["body"], TOKEN_LIMIT)
256189

257190
issues = [GitHubIssue(**issue) for issue in issues_data]
258191

0 commit comments

Comments
 (0)