Skip to content

Commit ff7e129

Browse files
authored
Merge branch 'main' into autogen_instrumentation
2 parents 33fbe27 + 593f99e commit ff7e129

14 files changed

Lines changed: 5505 additions & 551 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Compile llms.txt
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths:
7+
- 'docs/**'
8+
- 'README.md'
9+
- 'CONTRIBUTING.md'
10+
- 'examples/*/README.md'
11+
- 'agentops/*/README.md'
12+
workflow_dispatch:
13+
14+
jobs:
15+
compile-llms-txt:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
token: ${{ secrets.GITHUB_TOKEN }}
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v4
24+
with:
25+
python-version: '3.11'
26+
27+
- name: Install dependencies
28+
run: |
29+
pip install llms-txt
30+
31+
- name: Compile llms.txt
32+
run: |
33+
cd docs
34+
python compile_llms_txt.py
35+
36+
- name: Commit and push if changed
37+
run: |
38+
git config --local user.email "action@github.com"
39+
git config --local user.name "GitHub Action"
40+
git add llms.txt
41+
git diff --staged --quiet || git commit -m "Auto-update llms.txt from documentation changes"
42+
git push

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ agent = initialize_agent(tools,
349349
handle_parsing_errors=True)
350350
```
351351

352-
Check out the [Langchain Examples Notebook](./examples/langchain_examples.ipynb) for more details including Async handlers.
352+
Check out the [Langchain Examples Notebook](./examples/langchain/langchain_examples.ipynb) for more details including Async handlers.
353353

354354
</details>
355355

agentops/instrumentation/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ def _perform_instrumentation(package_name: str):
346346
_has_agentic_library = True
347347

348348
# Special case: If mem0 is instrumented, also instrument concurrent.futures
349-
if package_name == "mem0" and is_newly_added:
349+
if (package_name == "mem0" or package_name == "autogen") and is_newly_added:
350350
try:
351351
# Check if concurrent.futures module is available
352352

agentops/instrumentation/agentic/ag2/instrumentor.py

Lines changed: 472 additions & 82 deletions
Large diffs are not rendered by default.

agentops/instrumentation/agentic/agno/instrumentor.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -601,17 +601,7 @@ def wrapper(wrapped, instance, args, kwargs):
601601
for key, value in attributes.items():
602602
span.set_attribute(key, value)
603603

604-
# Execute the original function
605-
result = wrapped(*args, **kwargs)
606-
607-
# Set result attributes
608-
result_attributes = get_metrics_attributes(args=(instance,) + args, kwargs=kwargs, return_value=result)
609-
for key, value in result_attributes.items():
610-
if key not in attributes: # Avoid duplicates
611-
span.set_attribute(key, value)
612-
613604
span.set_status(Status(StatusCode.OK))
614-
return result
615605

616606
except Exception as e:
617607
span.set_status(Status(StatusCode.ERROR, str(e)))

docs/compile_llms_txt.py

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import os
2+
import re
3+
from pathlib import Path
4+
5+
6+
def clean_html_content(text):
7+
"""Remove HTML tags and clean content for llms.txt compatibility."""
8+
text = re.sub(r"<[^>]+>", "", text)
9+
10+
lines = text.split("\n")
11+
cleaned_lines = []
12+
in_table = False
13+
14+
for line in lines:
15+
stripped = line.strip()
16+
17+
if "|" in stripped and (stripped.startswith("|") or stripped.count("|") >= 2):
18+
in_table = True
19+
continue
20+
elif in_table and (stripped.startswith("-") or not stripped):
21+
continue
22+
else:
23+
in_table = False
24+
25+
cleaned_line = re.sub(r"[^\x00-\x7F]+", "", line)
26+
27+
if cleaned_line.strip() or (cleaned_lines and cleaned_lines[-1].strip()):
28+
cleaned_lines.append(cleaned_line)
29+
30+
return "\n".join(cleaned_lines)
31+
32+
33+
def convert_relative_urls(text, base_url="https://github.com/AgentOps-AI/agentops/blob/main"):
34+
"""Convert relative URLs to absolute URLs for llms.txt compliance."""
35+
36+
def replace_relative_link(match):
37+
link_text = match.group(1)
38+
url = match.group(2)
39+
40+
if url.startswith(("http://", "https://", "mailto:")):
41+
return match.group(0)
42+
43+
if url.startswith("#"):
44+
absolute_url = f"{base_url}/README.md{url}"
45+
return f"[{link_text}]({absolute_url})"
46+
47+
if url.startswith("./"):
48+
url = url[2:]
49+
elif url.startswith("../"):
50+
url = url[3:]
51+
52+
url = re.sub(r"/+", "/", url)
53+
url = url.strip("/")
54+
55+
if not url:
56+
return match.group(0)
57+
58+
absolute_url = f"{base_url}/{url}"
59+
return f"[{link_text}]({absolute_url})"
60+
61+
text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", replace_relative_link, text)
62+
63+
return text
64+
65+
66+
def compile_llms_txt():
67+
"""Compile a comprehensive llms.txt file with actual repository content."""
68+
69+
content = "# AgentOps\n\n"
70+
71+
content += "> AgentOps is the developer favorite platform for testing, debugging, and deploying AI agents and LLM apps. Monitor, analyze, and optimize your agent workflows with comprehensive observability and analytics.\n\n"
72+
73+
try:
74+
with open("../README.md", "r", encoding="utf-8") as f:
75+
readme_content = f.read()
76+
cleaned_readme = clean_html_content(readme_content)
77+
cleaned_readme = convert_relative_urls(cleaned_readme)
78+
content += "## Repository Overview\n\n"
79+
content += cleaned_readme + "\n\n"
80+
except Exception as e:
81+
print(f"Warning: Could not read README.md: {e}")
82+
83+
try:
84+
with open("../CONTRIBUTING.md", "r", encoding="utf-8") as f:
85+
contributing_content = f.read()
86+
cleaned_contributing = clean_html_content(contributing_content)
87+
cleaned_contributing = convert_relative_urls(cleaned_contributing)
88+
content += "## Contributing Guide\n\n"
89+
content += cleaned_contributing + "\n\n"
90+
except Exception as e:
91+
print(f"Warning: Could not read CONTRIBUTING.md: {e}")
92+
93+
content += "## Core SDK Implementation\n\n"
94+
95+
sdk_files = ["../agentops/__init__.py", "../agentops/client/client.py", "../agentops/sdk/decorators/__init__.py"]
96+
97+
for file_path in sdk_files:
98+
if os.path.exists(file_path):
99+
try:
100+
with open(file_path, "r", encoding="utf-8") as f:
101+
file_content = f.read()
102+
relative_path = os.path.relpath(file_path, "..")
103+
content += f"### {relative_path}\n\n```python\n{file_content}\n```\n\n"
104+
except Exception as e:
105+
print(f"Warning: Could not read {file_path}: {e}")
106+
107+
content += "## Documentation\n\n"
108+
109+
doc_files = ["v2/introduction.mdx", "v2/quickstart.mdx", "v2/concepts/core-concepts.mdx", "v1/quickstart.mdx"]
110+
111+
for doc_file in doc_files:
112+
if os.path.exists(doc_file):
113+
try:
114+
with open(doc_file, "r", encoding="utf-8") as f:
115+
file_content = f.read()
116+
cleaned_content = clean_html_content(file_content)
117+
cleaned_content = convert_relative_urls(cleaned_content)
118+
content += f"### {doc_file}\n\n{cleaned_content}\n\n"
119+
except Exception as e:
120+
print(f"Warning: Could not read {doc_file}: {e}")
121+
122+
content += "## Instrumentation Architecture\n\n"
123+
124+
instrumentation_files = [
125+
"../agentops/instrumentation/__init__.py",
126+
"../agentops/instrumentation/README.md",
127+
"../agentops/instrumentation/providers/openai/instrumentor.py",
128+
]
129+
130+
for file_path in instrumentation_files:
131+
if os.path.exists(file_path):
132+
try:
133+
with open(file_path, "r", encoding="utf-8") as f:
134+
file_content = f.read()
135+
relative_path = os.path.relpath(file_path, "..")
136+
if file_path.endswith(".py"):
137+
content += f"### {relative_path}\n\n```python\n{file_content}\n```\n\n"
138+
else:
139+
cleaned_content = clean_html_content(file_content)
140+
cleaned_content = convert_relative_urls(cleaned_content)
141+
content += f"### {relative_path}\n\n{cleaned_content}\n\n"
142+
except Exception as e:
143+
print(f"Warning: Could not read {file_path}: {e}")
144+
145+
content += "## Examples\n\n"
146+
147+
example_files = [
148+
"../examples/openai/openai_example_sync.py",
149+
"../examples/crewai/job_posting.py",
150+
"../examples/langchain/langchain_examples.py",
151+
"../examples/README.md",
152+
]
153+
154+
for file_path in example_files:
155+
if os.path.exists(file_path):
156+
try:
157+
with open(file_path, "r", encoding="utf-8") as f:
158+
file_content = f.read()
159+
relative_path = os.path.relpath(file_path, "..")
160+
if file_path.endswith(".py"):
161+
content += f"### {relative_path}\n\n```python\n{file_content}\n```\n\n"
162+
else:
163+
cleaned_content = clean_html_content(file_content)
164+
cleaned_content = convert_relative_urls(cleaned_content)
165+
content += f"### {relative_path}\n\n{cleaned_content}\n\n"
166+
except Exception as e:
167+
print(f"Warning: Could not read {file_path}: {e}")
168+
169+
output_path = Path("../llms.txt")
170+
output_path.write_text(content, encoding="utf-8")
171+
print(f"Successfully compiled comprehensive llms.txt to {output_path.absolute()}")
172+
print(f"Total content length: {len(content)} characters")
173+
174+
try:
175+
import llms_txt
176+
177+
print("SUCCESS: llms-txt package available for validation")
178+
179+
import re
180+
181+
link_pattern = r"\[([^\]]+)\]\(([^)]+)\)"
182+
links = re.findall(link_pattern, content)
183+
184+
has_h1 = content.startswith("# ")
185+
has_blockquote = "> " in content[:500] # Check first 500 chars for summary
186+
h2_count = content.count("\n## ")
187+
188+
title_match = re.match(r"^# (.+)$", content.split("\n")[0])
189+
title = title_match.group(1) if title_match else "Unknown"
190+
191+
summary_match = re.search(r"> (.+)", content)
192+
summary = summary_match.group(1) if summary_match else "No summary"
193+
194+
print("SUCCESS: Manual validation results:")
195+
print(f" - Title: {title}")
196+
print(f" - Summary: {summary[:100]}{'...' if len(summary) > 100 else ''}")
197+
print(f" - H2 sections: {h2_count}")
198+
print(f" - Links found: {len(links)}")
199+
print(f" - Content size: {len(content)} characters")
200+
201+
print("SUCCESS: Structure validation:")
202+
print(f" - H1 header: {'PASS' if has_h1 else 'FAIL'}")
203+
print(f" - Blockquote summary: {'PASS' if has_blockquote else 'FAIL'}")
204+
print(f" - Multiple sections: {'PASS' if h2_count > 0 else 'FAIL'}")
205+
206+
try:
207+
simple_test = "# Test\n\n> Test summary\n\n## Section\n\nContent here."
208+
llms_txt.parse_llms_file(simple_test)
209+
print("SUCCESS: llms-txt library functional (tested with simple content)")
210+
except Exception as simple_error:
211+
print(f"WARNING: llms-txt library has parsing issues: {simple_error}")
212+
213+
print("INFO: For comprehensive content validation, use: https://llmstxtvalidator.dev")
214+
215+
except ImportError:
216+
print("WARNING: llms-txt package not available, skipping library validation")
217+
print("INFO: Install with: pip install llms-txt")
218+
219+
220+
if __name__ == "__main__":
221+
compile_llms_txt()

0 commit comments

Comments
 (0)