diff --git a/docs/mint.json b/docs/mint.json
index bb384ef1..7a7b5333 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -51,10 +51,15 @@
}
],
"navigation": [
+ {
+ "group": "",
+ "pages": [
+ "v1/introduction"
+ ]
+ },
{
"group": "Getting Started",
"pages": [
- "v1/introduction",
"v1/quickstart",
"v1/examples"
]
@@ -72,6 +77,7 @@
{
"group": "Usage",
"pages": [
+ "v1/usage/dashboard-info",
"v1/usage/sdk-reference",
"v1/usage/environment-variables",
"v1/usage/tracking-llm-calls",
diff --git a/docs/snippets/add-code-tooltip.mdx b/docs/snippets/add-code-tooltip.mdx
new file mode 100644
index 00000000..2aa99c92
--- /dev/null
+++ b/docs/snippets/add-code-tooltip.mdx
@@ -0,0 +1,3 @@
+
+ Make sure to call `agentops.init` before calling any `openai`, `cohere`, `crew`, etc models.
+
\ No newline at end of file
diff --git a/docs/snippets/add-env-tooltip.mdx b/docs/snippets/add-env-tooltip.mdx
new file mode 100644
index 00000000..240a4019
--- /dev/null
+++ b/docs/snippets/add-env-tooltip.mdx
@@ -0,0 +1,3 @@
+
+ Set your API Key as an `.env` variable for easy access.
+
\ No newline at end of file
diff --git a/docs/snippets/claude-card.mdx b/docs/snippets/claude-card.mdx
new file mode 100644
index 00000000..a7e6583a
--- /dev/null
+++ b/docs/snippets/claude-card.mdx
@@ -0,0 +1,3 @@
+}>
+
+
\ No newline at end of file
diff --git a/docs/snippets/crewai-card.mdx b/docs/snippets/crewai-card.mdx
new file mode 100644
index 00000000..98190ed5
--- /dev/null
+++ b/docs/snippets/crewai-card.mdx
@@ -0,0 +1,3 @@
+}>
+
+
\ No newline at end of file
diff --git a/docs/snippets/discord-cta.mdx b/docs/snippets/discord-cta.mdx
new file mode 100644
index 00000000..c314f97d
--- /dev/null
+++ b/docs/snippets/discord-cta.mdx
@@ -0,0 +1,6 @@
+
+
+
+ Join our Discord community for the latest updates and support!
+
+
\ No newline at end of file
diff --git a/docs/snippets/github-stars.mdx b/docs/snippets/github-stars.mdx
new file mode 100644
index 00000000..e31a312c
--- /dev/null
+++ b/docs/snippets/github-stars.mdx
@@ -0,0 +1 @@
+Look useful? [Star us on Github](https://github.com/AgentOps-AI/agentops)! (you may be our 2,000th 😊)
\ No newline at end of file
diff --git a/docs/snippets/no-mintlify-cards.mdx b/docs/snippets/no-mintlify-cards.mdx
new file mode 100644
index 00000000..c65d22fb
--- /dev/null
+++ b/docs/snippets/no-mintlify-cards.mdx
@@ -0,0 +1,33 @@
+
+
+
+
+
OpenAI automates my job posts...
+
+
+
but with a 50% error rate!
+
+
+
+
+
+
+
Crew boosts my product on Reddit...
+
+
+
but misses key subreddits!
+
+
+
+
+
+
+
Claude auto-replies to my emails...
+
+
+
but has the wrong tone!
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/snippets/openai-card.mdx b/docs/snippets/openai-card.mdx
new file mode 100644
index 00000000..89a7c006
--- /dev/null
+++ b/docs/snippets/openai-card.mdx
@@ -0,0 +1,3 @@
+}>
+
+
\ No newline at end of file
diff --git a/docs/snippets/openai-icon.mdx b/docs/snippets/openai-icon.mdx
new file mode 100644
index 00000000..7ccef050
--- /dev/null
+++ b/docs/snippets/openai-icon.mdx
@@ -0,0 +1,3 @@
+}>
+
+
\ No newline at end of file
diff --git a/docs/v1/examples.mdx b/docs/v1/examples.mdx
index 3abbe554..9560e569 100644
--- a/docs/v1/examples.mdx
+++ b/docs/v1/examples.mdx
@@ -49,4 +49,7 @@ mode: "wide"
-
\ No newline at end of file
+
+
+
+
diff --git a/docs/v1/img/chart-waterfall.png b/docs/v1/img/chart-waterfall.png
index 459163e8..caa553a7 100644
Binary files a/docs/v1/img/chart-waterfall.png and b/docs/v1/img/chart-waterfall.png differ
diff --git a/docs/v1/img/discord.png b/docs/v1/img/discord.png
new file mode 100644
index 00000000..e886e8da
Binary files /dev/null and b/docs/v1/img/discord.png differ
diff --git a/docs/v1/img/docs-icons/chat.png b/docs/v1/img/docs-icons/chat.png
new file mode 100644
index 00000000..c12465c7
Binary files /dev/null and b/docs/v1/img/docs-icons/chat.png differ
diff --git a/docs/v1/img/docs-icons/claude.png b/docs/v1/img/docs-icons/claude.png
new file mode 100644
index 00000000..abd83edd
Binary files /dev/null and b/docs/v1/img/docs-icons/claude.png differ
diff --git a/docs/v1/img/docs-icons/crew.png b/docs/v1/img/docs-icons/crew.png
new file mode 100644
index 00000000..bc50c6bf
Binary files /dev/null and b/docs/v1/img/docs-icons/crew.png differ
diff --git a/docs/v1/integrations/autogen.mdx b/docs/v1/integrations/autogen.mdx
index 6d6f2eec..4e39cc1f 100644
--- a/docs/v1/integrations/autogen.mdx
+++ b/docs/v1/integrations/autogen.mdx
@@ -3,6 +3,9 @@ title: Autogen
description: "AgentOps provides first class support for Autogen"
---
+import CodeTooltip from '/snippets/add-code-tooltip.mdx'
+import EnvTooltip from '/snippets/add-env-tooltip.mdx'
+
AgentOps and Autogen teamed up to make monitoring Autogen agents dead simple.
Autogen has comprehensive [documentation](https://microsoft.github.io/autogen/docs) available as well as a great [quickstart](https://microsoft.github.io/autogen/docs/Getting-Started).
@@ -32,43 +35,29 @@ Autogen has comprehensive [documentation](https://microsoft.github.io/autogen/do
- 1. Before setting up anything in Autogen, call `agentops.init()`
- 2. At the end of your agent run, call `agentops.end_session("Success")`
-
- ```python python
- import agentops
-
- # Beginning of program (i.e. main.py, __init__.py)
- # IMPORTANT: Must be before using any autogen setup
- agentops.init()
- ...
- # End of program (e.g. main.py)
- agentops.end_session("Success") # Success|Fail|Indeterminate
- ```
-
-
- Instantiating the AgentOps client will automatically instrument Autogen, meaning you will be able to see all
- of your sessions on the AgentOps Dashboard along with the full LLM chat histories, cost, token counts, etc.
-
-
- For more features see our [Usage](/v1/usage) section.
-
-
-
- Retrieve an API Key from your Settings > [Projects & API Keys](https://app.agentops.ai/settings/projects) page.
-
-
-
-
- API keys are tied to individual projects.
- A Default Project has been created for you, so just click Copy API Key
-
- Set this API Key in your [environment variables](/v1/usage/environment-variables)
- ```python .env
- AGENTOPS_API_KEY=
- ```
+
+
+
+ ```python python
+ import agentops
+ agentops.init()
+ ...
+ # MUST END SESSION at end of program (e.g. main.py)
+ agentops.end_session("Success") # Success|Fail|Indeterminate
+ ```
+
+
+
+
+
+ ```python .env
+ AGENTOPS_API_KEY=
+ ```
+
+ Read more about [environment variables](/v1/usage/environment-variables)
+
-
+
Execute your program and visit [app.agentops.ai/drilldown](https://app.agentops.ai/drilldown) to observe your Autogen Agent! 🕵️
After your run, AgentOps prints a clickable url to console linking directly to your session in the Dashboard
@@ -81,4 +70,8 @@ Autogen has comprehensive [documentation](https://microsoft.github.io/autogen/do
-
\ No newline at end of file
+
+
+
+
+
diff --git a/docs/v1/integrations/cohere.mdx b/docs/v1/integrations/cohere.mdx
index 87210186..35ae2ca0 100644
--- a/docs/v1/integrations/cohere.mdx
+++ b/docs/v1/integrations/cohere.mdx
@@ -3,6 +3,9 @@ title: Cohere
description: "AgentOps provides first class support for Cohere"
---
+import CodeTooltip from '/snippets/add-code-tooltip.mdx'
+import EnvTooltip from '/snippets/add-env-tooltip.mdx'
+
This is a living integration. Should you need any added functionality message us on [Discord](https://discord.gg/UgJyyxx7uc)!
@@ -25,12 +28,11 @@ This is a living integration. Should you need any added functionality message us
[Give us a star](https://github.com/AgentOps-AI/agentops) on GitHub while you're at it (you may be our 2,000th 😊)
+
+
```python python
import agentops
-
- # Beginning of program (i.e. main.py, __init__.py)
- # IMPORTANT: Must be before calling `co = cohere.Client()`
agentops.init()
co = cohere.Client()
...
@@ -38,32 +40,21 @@ This is a living integration. Should you need any added functionality message us
agentops.end_session("Success") # Success|Fail|Indeterminate
```
+
Requires cohere>=5.4.0
-
- Instantiating the AgentOps client will automatically instrument Cohere, meaning you will be able to see all
- of your sessions on the AgentOps Dashboard along with the full LLM chat histories, cost, token counts, etc.
-
-
- For more features see our [Usage](/v1/usage) section.
-
-
-
- Retrieve an API Key from your Settings > [Projects & API Keys](https://app.agentops.ai/settings/projects) page.
-
-
-
-
- API keys are tied to individual projects.
- A Default Project has been created for you, so just click Copy API Key
-
- Set this API Key in your [environment variables](/v1/usage/environment-variables)
- ```python .env
- AGENTOPS_API_KEY=
- ```
+
+
+
+ ```python .env
+ AGENTOPS_API_KEY=
+ ```
+
+ Read more about [environment variables](/v1/usage/environment-variables)
+
-
+
Execute your program and visit [app.agentops.ai/drilldown](https://app.agentops.ai/drilldown) to observe your Agents! 🕵️
After your run, AgentOps prints a clickable url to console linking directly to your session in the Dashboard
@@ -116,4 +107,8 @@ This is a living integration. Should you need any added functionality message us
-
\ No newline at end of file
+
+
+
+
+
diff --git a/docs/v1/integrations/crewai.mdx b/docs/v1/integrations/crewai.mdx
index 68535f4f..07f7c3ac 100644
--- a/docs/v1/integrations/crewai.mdx
+++ b/docs/v1/integrations/crewai.mdx
@@ -3,7 +3,10 @@ title: 'CrewAI'
description: '[CrewAI](https://crewai.io) is a framework for easily building multi-agent applications.'
---
-Crew and AgentOps have a first class partnership, adding agent observability to all Crew runs with 3 lines of code.
+import CodeTooltip from '/snippets/add-code-tooltip.mdx'
+import EnvTooltip from '/snippets/add-env-tooltip.mdx'
+
+AgentOps and CrewAI teamed up to make monitoring Crew agents dead simple. Crew and AgentOps have a first class partnership, adding agent observability to all Crew runs with 3 lines of code.
CrewAI multi-agent framework with AgentOps support
@@ -37,43 +40,29 @@ Crew has comprehensive [documentation](https://docs.crewai.com) available as wel
- 1. Before calling the `Crew()` constructor in your code, call `agentops.init()`
- 2. At the end of your Crew run, call `agentops.end_session("Success")`
-
- ```python python
- import agentops
-
- # Beginning of program (i.e. main.py, __init__.py)
- # IMPORTANT: Must be before calling the `Crew()` constructor
- agentops.init()
- ...
- # End of program (e.g. main.py)
- agentops.end_session("Success") # Success|Fail|Indeterminate
- ```
-
-
- Instantiating the AgentOps client will automatically instrument Crew, meaning you will be able to see all
- of your sessions on the AgentOps Dashboard along with the full LLM chat histories, cost, token counts, etc.
-
-
- For more features see our [Usage](/v1/usage) section.
-
-
-
- Retrieve an API Key from your Settings > [Projects & API Keys](https://app.agentops.ai/settings/projects) page.
-
-
-
-
- API keys are tied to individual projects.
- A Default Project has been created for you, so just click Copy API Key
-
- Set this API Key in your [environment variables](/v1/usage/environment-variables)
- ```python .env
- AGENTOPS_API_KEY=
- ```
+
+
+
+ ```python python
+ import agentops
+ agentops.init()
+ ...
+ # MUST END SESSION at end of program (e.g. main.py)
+ agentops.end_session("Success") # Success|Fail|Indeterminate
+ ```
+
+
+
+
+
+ ```python .env
+ AGENTOPS_API_KEY=
+ ```
+
+ Read more about [environment variables](/v1/usage/environment-variables)
+
-
+
Execute your program and visit [app.agentops.ai/drilldown](https://app.agentops.ai/drilldown) to observe your Crew! 🕵️
After your run, AgentOps prints a clickable url to console linking directly to your session in the Dashboard
@@ -102,4 +91,8 @@ agentops.init(skip_auto_end_session=True)
-
\ No newline at end of file
+
+
+
+
+
diff --git a/docs/v1/integrations/langchain.mdx b/docs/v1/integrations/langchain.mdx
index b0b5ff1c..575091f3 100644
--- a/docs/v1/integrations/langchain.mdx
+++ b/docs/v1/integrations/langchain.mdx
@@ -3,6 +3,8 @@ title: Langchain
description: "AgentOps provides first class support for Lanchain applications"
---
+import EnvTooltip from '/snippets/add-env-tooltip.mdx'
+
AgentOps works seamlessly with applications built using Langchain.
## Adding AgentOps to Langchain applications
@@ -31,56 +33,52 @@ AgentOps works seamlessly with applications built using Langchain.
from agentops.langchain_callback_handler import LangchainCallbackHandler
```
-
- For more features see our [Usage](/v1/usage) section.
-
- Set up your Langchain agent with the AgentOps callback handler and AgentOps will automatically record your Langchain sessions.
-
- ```python python
- handler = LangchainCallbackHandler(api_key=AGENTOPS_API_KEY, tags=['Langchain Example'])
-
- llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY,
- callbacks=[handler],
- model='gpt-3.5-turbo')
-
- agent = initialize_agent(tools,
- llm,
- agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION,
- verbose=True,
- callbacks=[handler], # You must pass in a callback handler to record your agent
- handle_parsing_errors=True)
- ```
-
Note that you don't need to set up a separate agentops.init() call, as the Langchain callback handler will automatically initialize the AgentOps client for you.
+ Set up your Langchain agent with the AgentOps callback handler and AgentOps will automatically record your Langchain sessions.
+
+
+ ```python python
+ handler = LangchainCallbackHandler(api_key=AGENTOPS_API_KEY, tags=['Langchain Example'])
+
+
+
+ llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY,
+ callbacks=[handler],
+ model='gpt-3.5-turbo')
+
+ agent = initialize_agent(tools,
+ llm,
+ agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION,
+ verbose=True,
+ callbacks=[handler], # You must pass in a callback handler to record your agent
+ handle_parsing_errors=True)
+ ```
+
+
+
+
+
+ ```python .env
+ AGENTOPS_API_KEY=
+ ```
+
+ Read more about [environment variables](/v1/usage/environment-variables)
+
-
- Retrieve an API Key from your Settings > [Projects & API Keys](https://app.agentops.ai/settings/projects) page.
-
-
-
-
- API keys are tied to individual projects.
- A Default Project has been created for you, so just click Copy API Key
-
- Set this API Key in your [environment variables](/v1/usage/environment-variables)
- ```python .env
- AGENTOPS_API_KEY=
- ```
-
-
- Execute your program and visit [app.agentops.ai/drilldown](https://app.agentops.ai/drilldown) to observe your Langchain Agent! 🕵️
-
- After your run, AgentOps prints a clickable url to console linking directly to your session in the Dashboard
-
- {/* Intentionally blank div for newline */}
-
-
-
-
+
+ Execute your program and visit [app.agentops.ai/drilldown](https://app.agentops.ai/drilldown) to observe your Langchain Agent! 🕵️
+
+ After your run, AgentOps prints a clickable url to console linking directly to your session in the Dashboard
+
+ {/* Intentionally blank div for newline */}
+
+
+
+
## Full Examples
@@ -108,4 +106,8 @@ AgentOps works seamlessly with applications built using Langchain.
-
\ No newline at end of file
+
+
+
+
+
diff --git a/docs/v1/integrations/litellm.mdx b/docs/v1/integrations/litellm.mdx
index b0efde27..8ce75369 100644
--- a/docs/v1/integrations/litellm.mdx
+++ b/docs/v1/integrations/litellm.mdx
@@ -41,4 +41,8 @@ response = await litellm.acompletion(model="claude-3", messages=messages)
```
-
\ No newline at end of file
+
+
+
+
+
diff --git a/docs/v1/introduction.mdx b/docs/v1/introduction.mdx
index 2a901521..f269be0e 100644
--- a/docs/v1/introduction.mdx
+++ b/docs/v1/introduction.mdx
@@ -1,71 +1,65 @@
---
title: "Introduction"
-description: "Build your next agent with evals, observability, and replays"
+description: "Find where your agents went wrong."
mode: "wide"
---
-[Give us a star](https://github.com/AgentOps-AI/agentops) on GitHub if you find AgentOps helpful (you may be our 2,000th 😊)
+import GithubStars from '/snippets/github-stars.mdx'
+import DiscordCTA from '/snippets/discord-cta.mdx'
-# AgentOps solves what Terminal can't
+## AI makes mistakes...
+#### .. but your terminal can't detect them
-
-
- - • Track agents across executions
- - • Parse out LLM completions from output logs
- - • Give you insight into what your agents did
-
-
- - • Record LLM prompts, completions, & timestamps
- - • Log events, calls, and any other agent activity
- - • Link agent errors back to their causal event
-
-
+
+
+
+
>
M
M
l
_
+
+
+
+
+
-And we do it all in just two lines of code...
-
- ```python python
- import agentops
- agentops.init()
- ```
-
-... that logs everything back to your AgentOps Dashboard.
+AI's biggest problem is that it flies blind... but how can you fix what you can't even see? 👀
-## The AgentOps Dashboard
+## Let's give you full visibility
+#### AgentOps logs each step of the AI's decision and helps you understand where it went wrong.
-With just two lines of code, you can free yourself from the chains of the terminal and instead visualize your agents' behavior
-in your AgentOps Dashboard. After setting up AgentOps, each execution of your program is recorded as a session and the above
-data is automatically recorded for you.
-
-The examples below were captured with two lines of code.
+
+
+
-### Session Drilldown
-Here you will find a list of all of your previously recorded sessions and useful data about each such as total execution time.
-You also get helpful debugging info such as any SDK versions you were on if you're building on a supported agent framework like Crew or AutoGen.
-LLM calls are presented as a familiar chat history view, and charts give you a breakdown of the types of events that were called and how long they took.
+## Install AgentOps
-
-
-
+
+
+ ```python python
+ pip install agentops
+ ```
+
+
-Find any past sessions from your Session Drawer.
-
-
-
+### 🤯 And use it with just 2 lines of code!
-Most powerful of all is the Session Waterfall. On the left, a time visualization of all your LLM calls, Action events, Tool calls, and Errors.
-On the right, specific details about the event you've selected on the waterfall. For instance the exact prompt and completion for a given LLM call.
-Most of which has been automatically recorded for you.
+
+
+ ```python python
+ import agentops
+ agentops.init()
+ ```
+
+
-
-
-
+
+
+ Get Started
+
-### Session Overview
-View a meta-analysis of all of your sessions in a single view.
-
-
-
+
-
\ No newline at end of file
+
+
+
+
diff --git a/docs/v1/quickstart.mdx b/docs/v1/quickstart.mdx
index 11c5a236..9f8cf575 100644
--- a/docs/v1/quickstart.mdx
+++ b/docs/v1/quickstart.mdx
@@ -2,7 +2,8 @@
title: "Quickstart"
description: "Start using AgentOps with just 2 lines of code"
---
-import SupportedModels from '/snippets/supported-models.mdx'
+import CodeTooltip from '/snippets/add-code-tooltip.mdx'
+import EnvTooltip from '/snippets/add-env-tooltip.mdx'
@@ -16,38 +17,24 @@ import SupportedModels from '/snippets/supported-models.mdx'
-
- ```python python
- import agentops
-
- # Beginning of program (i.e. main.py, __init__.py)
- # IMPORTANT: Must be before any OpenAI, Cohere, Crew, etc constructors are called
- # e.g. before client = OpenAI(...)
- agentops.init()
- ```
-
- Just these two lines and you have magically tracked LLM calls!
-
- After the `openai`, `cohere`, or `litellm` packages have been imported, importing + instantiating the AgentOps client
- will automatically instrument them, meaning you will be able to see all of your sessions on the AgentOps Dashboard
- along with the full LLM chat histories, cost, token counts, etc.
-
-
-
-
- Retrieve an API Key from your Settings > [Projects & API Keys](https://app.agentops.ai/settings/projects) page.
-
-
-
-
-
- API keys are tied to individual projects.
- A Default Project has been created for you, so just click Copy API Key
-
- Set this API Key in your [environment variables](/v1/usage/environment-variables)
- ```python .env
- AGENTOPS_API_KEY=
- ```
+
+
+
+ ```python python
+ import agentops
+ agentops.init()
+ ```
+
+
+
+
+
+ ```python .env
+ AGENTOPS_API_KEY=
+ ```
+
+ Read more about [environment variables](/v1/usage/environment-variables)
+
Execute your program and visit [app.agentops.ai/drilldown](https://app.agentops.ai/drilldown) to observe your Agent! 🕵️
@@ -155,4 +142,8 @@ agentops.end_session('Success')
-
\ No newline at end of file
+
+
+
+
+
diff --git a/docs/v1/scripts/button_heartbeat_animation.js b/docs/v1/scripts/button_heartbeat_animation.js
new file mode 100644
index 00000000..bde19b81
--- /dev/null
+++ b/docs/v1/scripts/button_heartbeat_animation.js
@@ -0,0 +1,17 @@
+// Placeholder for anime.js animation
+
+function addHeartbeatListeners() {
+ setTimeout(() => {
+ document.querySelectorAll('li, a').forEach(element => {
+ element.addEventListener('click', function() {
+ drawHeartbeat();
+ });
+ });
+ }, 50);
+}
+
+window.addEventListener('load', function() {
+ drawHeartbeat();
+ const observer = new MutationObserver(addHeartbeatListeners);
+ observer.observe(document.body, { childList: true, subtree: true });
+});
\ No newline at end of file
diff --git a/docs/v1/scripts/github_stars.js b/docs/v1/scripts/github_stars.js
new file mode 100644
index 00000000..6b8e6b5c
--- /dev/null
+++ b/docs/v1/scripts/github_stars.js
@@ -0,0 +1,28 @@
+function updateStars() {
+ fetch("https://api.github.com/repos/AgentOps-AI/agentops")
+ .then((response) => response.json())
+ .then((data) => {
+ const stars = Math.ceil(data.stargazers_count / 100) * 100 + 100;
+ setTimeout(() => {
+ const dataContainer = document.getElementById("stars-text");
+ dataContainer.innerHTML = `${stars.toLocaleString()}th`;
+ }, 50);
+ })
+ .catch((error) => {
+ // console.error("Github Stars pull error:", error);
+ });
+}
+
+function addNewEventListeners() {
+ setTimeout(() => {
+ document.querySelectorAll('li, a').forEach(element => {
+ element.addEventListener('click', updateStars);
+ });
+ }, 50);
+}
+
+window.addEventListener('load', function() {
+ updateStars();
+ const observer = new MutationObserver(addNewEventListeners);
+ observer.observe(document.body, { childList: true, subtree: true });
+});
\ No newline at end of file
diff --git a/docs/v1/scripts/link_to_api_button.js b/docs/v1/scripts/link_to_api_button.js
new file mode 100644
index 00000000..fb9be5b5
--- /dev/null
+++ b/docs/v1/scripts/link_to_api_button.js
@@ -0,0 +1,55 @@
+// NOTE: The copy button is copying the old non-displayed API key, need to add a click listener and set
+// the new textContent to be copied
+
+function patternFindAndAddButton(apiKeyButtonHTML) {
+ const targetClasses = document.querySelectorAll(".api-key-container");
+ const codeElements = [];
+ targetClasses.forEach(element => {
+ codeElements.push(element.querySelector('code'));
+ });
+
+ const apiKeyPattern = /AGENTOPS_API_KEY/g;
+ const tokenOperatorPattern1 = /<<\/span>YOUR API KEY><\/span>/g;
+ const tokenOperatorPattern2 = /<<\/span>INSERT YOUR API KEY HERE><\/span>/g;
+
+ // Process the collected elements
+ codeElements.forEach(element => {
+ console.log(element.textContent)
+ const containsApiKeyPattern = element.textContent.includes("AGENTOPS_API_KEY");
+ const containsTokenOperatorPattern1 = element.textContent.includes("");
+ const containsTokenOperatorPattern2 = element.textContent.includes("");
+
+ if (containsTokenOperatorPattern1 || containsTokenOperatorPattern2 || containsApiKeyPattern) {
+ const button = document.createElement('a');
+ button.className = 'api-key-button';
+ button.href = "https://app.agentops.ai/settings/projects";
+ button.target = "_blank";
+ button.textContent = 'Get API Key';
+ element.parentNode.parentNode.parentNode.insertBefore(button, element.nextSibling);
+ if (containsTokenOperatorPattern1) {
+ button.classList.add('pattern1');
+ } else if (containsTokenOperatorPattern2) {
+ button.classList.add('pattern2');
+ } else if (containsApiKeyPattern) {
+ button.classList.add('pattern0');
+ }
+ }
+ });
+}
+
+// Note that functions cannot be named the same thing across script files if copy-pasting this to add new scripts
+function addAPIEventListeners() { // would change to button
+ console.log("new listeners added")
+ document.querySelectorAll('li, a').forEach(element => {
+ // this can instead add a button before the "adjust-api-key" span that when clicked adjusts the API key
+ element.addEventListener('click', () => {
+ setTimeout(patternFindAndAddButton, 50);
+ });
+ });
+}
+
+window.addEventListener('load', function() {
+ patternFindAndAddButton();
+ const apiObserver = new MutationObserver(addAPIEventListeners);
+ apiObserver.observe(document.body, { childList: true, subtree: true });
+});
\ No newline at end of file
diff --git a/docs/v1/scripts/scroll-img-fadein-animation.js b/docs/v1/scripts/scroll-img-fadein-animation.js
new file mode 100644
index 00000000..742da7a6
--- /dev/null
+++ b/docs/v1/scripts/scroll-img-fadein-animation.js
@@ -0,0 +1,352 @@
+function typeInTerminalText() {
+ const pythonTerminal = document.getElementById("python-terminal");
+ const terminalCommand = document.getElementById("terminal-command");
+ const textsAndElementTargets = [
+ {
+ text: "python",
+ target: pythonTerminal,
+ nextTarget: {
+ text: "openai_job_post_agent.py",
+ target: terminalCommand
+ }
+ },
+ {
+ text: "python",
+ target: pythonTerminal,
+ nextTarget: {
+ text: "crew_subreddit_finder_agent.py",
+ target: terminalCommand
+ }
+ },
+ {
+ text: "python",
+ target: pythonTerminal,
+ nextTarget: {
+ text: "claude_email_reply_agent.py",
+ target: terminalCommand
+ }
+ }
+ ]
+ const toggleEmptySpace = document.getElementById("toggle-empty-space");
+ const inlineCodeImg = document.getElementById("inline-code-img");
+ const barelySpace = document.getElementById("barely-space");
+ const cursor = document.getElementById("cursor");
+ const terminalContentDiv = document.getElementById("terminal-content");
+ const terminalContentTexts = [
+ [
+ "Successfully posted 32 jobs ✅
",
+ "Failed to post 36 jobs ❌
",
+ "0 errors logged 📝
",
+ ],
+ [
+ "Found 18 relevant subreddits 🎯
",
+ "But 47 irrelevant ones 💩
",
+ "0 hallucinations logged 😵💫
",
+ ],
+ [
+ "10 perfectly polite emails 🧑💼
",
+ "3 uses of [$% - redacted] 🤬
",
+ "0 additional insight 🤔
"
+ ]
+ ]
+ const imagesSrcs = [
+ "https://github.com/AgentOps-AI/agentops/blob/3c03341f5129f9f494ca64ed4e8d03b9a0575db4/docs/images/docs-icons/chat.png?raw=true",
+ "https://github.com/AgentOps-AI/agentops/blob/388a8a94603393cd2aa15e1adcd56e7f435839f9/docs/images/docs-icons/crew.png?raw=true",
+ "https://github.com/AgentOps-AI/agentops/blob/3c03341f5129f9f494ca64ed4e8d03b9a0575db4/docs/images/docs-icons/claude.png?raw=true"
+ ]
+ let increment = 0;
+ let contentIncrement = 0;
+
+ function addToTerminal(terminalContentDiv, texts) {
+ let increment = 0;
+ const interval = setInterval(() => {
+ terminalContentDiv.innerHTML += texts[increment];
+ increment++;
+ if (increment === texts.length) {
+ clearInterval(interval);
+ cursor.classList.remove("off");
+ setTimeout(() => {
+ terminalContentDiv.innerHTML = null;
+ pythonTerminal.textContent = "";
+ terminalCommand.textContent = "";
+ inlineCodeImg.classList.remove("on");
+ barelySpace.classList.add("off");
+ contentIncrement = (contentIncrement + 1) % 3;
+ }, 4000);
+ }
+ }, 1200);
+ }
+
+ function typewriterEffect(text, target, nextTarget = null) {
+ let i = 0;
+ const interval = setInterval(() => {
+ if (i < text.length) {
+ target.textContent += text[i];
+ i++;
+ } else {
+ toggleEmptySpace.classList.remove("off");
+ inlineCodeImg.classList.add("on");
+ barelySpace.classList.remove("off");
+ clearInterval(interval);
+ if (nextTarget) {
+ setTimeout(() => {
+ typewriterEffect(nextTarget.text, nextTarget.target);
+ }, 20);
+ } else {
+ cursor.classList.add("off");
+ }
+ }
+ }, 20);
+ }
+
+ typewriterEffect(textsAndElementTargets[contentIncrement].text, textsAndElementTargets[contentIncrement].target, textsAndElementTargets[contentIncrement].nextTarget);
+ addToTerminal(terminalContentDiv, terminalContentTexts[contentIncrement]);
+
+ setInterval(() => {
+ // this is to catch timeout bugginess with Mint
+ terminalContentDiv.innerHTML = null;
+ pythonTerminal.textContent = "";
+ terminalCommand.textContent = "";
+ inlineCodeImg.classList.remove("on");
+ barelySpace.classList.add("off");
+
+ inlineCodeImg.src = imagesSrcs[contentIncrement];
+ typewriterEffect(textsAndElementTargets[contentIncrement].text, textsAndElementTargets[contentIncrement].target, textsAndElementTargets[contentIncrement].nextTarget);
+ addToTerminal(terminalContentDiv, terminalContentTexts[contentIncrement]);
+ }, 10000);
+}
+
+function typeInPipText(target) {
+ const codeTextFirstPart = "pip install agentops\n\n";
+ const codeTextSecondPartComplete = "[▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇] 100% √";
+ const LLMCostCodeText = "\nAgentOps: This LLM run cost $0.26270 √";
+ const dashboardText = "\nSee dashboard for full replay: https://app.agentops.ai/drilldown?session=SESSION_ID √";
+ let codeSecondIndex = 0;
+ target.textContent += "[ ] 0%";
+ const progressBar = "[ ] 0%";
+ const progressBarArray = progressBar.split('');
+ const progressBarLength = progressBar.length;
+
+ const codeTypewriterIntervalSecondPart = setInterval(() => {
+ if (target.textContent.includes("99%")) {
+ clearInterval(codeTypewriterIntervalSecondPart);
+ target.innerHTML = codeTextFirstPart + codeTextSecondPartComplete;
+ return;
+ }
+ if (codeSecondIndex < progressBarLength) {
+ if (progressBarArray[codeSecondIndex] === ' ' && codeSecondIndex % 2 === 1) {
+ progressBarArray[codeSecondIndex] = '▇';
+ }
+
+ let percentageIndexEnd = progressBarArray.indexOf('%');
+ let percentage = parseInt(progressBarArray.slice(percentageIndexEnd - 2, percentageIndexEnd).join('')) + 3;
+ let percentageString = percentage.toString().padStart(3, ' ');
+
+ target.textContent = codeTextFirstPart + progressBarArray.join('');
+ codeSecondIndex++;
+
+ for (let i = 0; i < percentageString.length; i++) {
+ progressBarArray[percentageIndexEnd - 3 + i] = percentageString[i];
+ }
+ } else {
+ clearInterval(codeTypewriterIntervalSecondPart);
+ }
+ }, 20);
+
+ const animateTokenCost = setTimeout(() => {
+ target.innerHTML += "\n\nObserving";
+ let dotCount = 0;
+ const dotInterval = setInterval(() => {
+ if (dotCount < 6) {
+ target.innerHTML += " .";
+ dotCount++;
+ } else {
+ target.innerHTML += " √"
+ clearInterval(dotInterval);
+ setTimeout(() => {
+ target.innerHTML += LLMCostCodeText;
+ setTimeout(() => {
+ target.innerHTML += dashboardText;
+ }, 800);
+ }, 800);
+ }
+ }, 200);
+
+ }, 1200);
+}
+
+function typeInGithubPushText(target) {
+ const codeTextSecondPart = "\nPushing to prod...";
+ const codeTextReplaceSecondPart = "\nPushed to prod √";
+ const codeTextThirdPart = "\n+2 ■■■■■";
+ const monitoringOnText = "\n\nEvents logged to dashboard ⬤";
+ const monitoringBlinkText = "\n\nEvents logged to dashboard ⬤";
+
+ const animateGithubPushText = setTimeout(() => {
+ target.innerHTML += codeTextSecondPart;
+ setTimeout(() => {
+ const textToReplaceElement = document.getElementById('text-to-replace');
+ if (textToReplaceElement) {
+ textToReplaceElement.remove();
+ }
+ target.innerHTML += codeTextReplaceSecondPart;
+ setTimeout(() => {
+ target.innerHTML += codeTextThirdPart;
+ setTimeout(() => {
+ target.innerHTML += monitoringBlinkText;
+ const monitoringOnInterval = setInterval(() => {
+ const monitoringOnTextElement = document.querySelector('.text-to-alternate');
+ if (monitoringOnTextElement) {
+ monitoringOnTextElement.remove();
+ }
+ target.innerHTML += monitoringOnText;
+ setTimeout(() => {
+ const monitoringBlinkTextElement = document.querySelector('.text-to-alternate');
+ if (monitoringBlinkTextElement) {
+ monitoringBlinkTextElement.remove();
+ }
+ target.innerHTML += monitoringBlinkText;
+ }, 800);
+ }, 1600);
+ }, 800);
+ }, 800);
+ }, 1200);
+ }, 300);
+}
+
+function fadeInIntroRowTexts(preloaded=false) {
+ function fadeInText(i) {
+ trailingTextElements[i].classList.add('loaded');
+ trailingImgElements[i].classList.add('loaded');
+ }
+
+ const trailingTextElements = document.getElementsByClassName('trailing');
+ const trailingImgElements = document.getElementsByClassName('trailing-img');
+
+ if (preloaded) {
+ for (let i = 0; i < trailingTextElements.length; i++) {
+ trailingTextElements[i].classList.add('preloaded');
+ trailingImgElements[i].classList.add('preloaded');
+ }
+ } else {
+ for (let i = 0; i < trailingTextElements.length; i++) {
+ const increment = i*500;
+ setTimeout(() => {
+ fadeInText(i);
+ }, increment);
+ }
+ }
+}
+
+function addIntroEventListeners() {
+ setTimeout(() => {
+ document.querySelectorAll('li, a').forEach(element => {
+ element.addEventListener('click', () => {
+ // Clear all intervals on click
+ const highestIntervalId = setInterval(() => {}, 1000);
+ for (let i = 0; i < highestIntervalId; i++) {
+ clearInterval(i);
+ }
+ setTimeout(() => {
+ loadTerminalText();
+ loadIntro(true);
+ fadeInIntroRowTexts(true);
+ }, 50);
+ });
+ });
+ }, 100);
+}
+
+function loadTerminalText() {
+ setInterval(() => {
+ const cursor = document.getElementById("cursor");
+ if (cursor.style.opacity === "0") {
+ cursor.style.opacity = "1";
+ } else {
+ cursor.style.opacity = "0";
+ }
+ }, 800);
+ setTimeout(() => {
+ typeInTerminalText();
+ }, 1000);
+}
+
+function loadIntro(preloaded = false) {
+ const targetSpans = document.querySelectorAll('.animated-code');
+ const targetSpansArray = Array.from(targetSpans);
+
+ if (!targetSpans) {
+ return;
+ }
+
+ const codeElementsArray = [];
+ targetSpansArray.forEach(targetSpan => {
+ const codeElement = targetSpan.querySelector('code');
+ console.log(codeElement);
+ codeElementsArray.push(codeElement);
+ });
+
+ if (preloaded) {
+ codeElementsArray[0].innerHTML = "pip install agentops\n\n[▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇ ▇] 100% √\n\nObserving . . . . . √\nAgentOps: This LLM run cost $0.26270 √\nSee dashboard for full replay: https://app.agentops.ai/drilldown?session=SESSION_ID √";
+ codeElementsArray[1].innerHTML = "import agentops\nagentops.init()\n\nPushed to prod √\n\n+2 ■■■■■\nEvents logged to dashboard ⬤";
+ const monitoringOnInterval = setInterval(() => {
+ const monitoringOnTextElement = document.querySelector('.text-to-alternate');
+ if (monitoringOnTextElement) {
+ monitoringOnTextElement.remove();
+ }
+ codeElementsArray[1].innerHTML += "Events logged to dashboard ⬤";
+ setTimeout(() => {
+ const monitoringBlinkTextElement = document.querySelector('.text-to-alternate');
+ if (monitoringBlinkTextElement) {
+ monitoringBlinkTextElement.remove();
+ }
+ codeElementsArray[1].innerHTML += "Events logged to dashboard ⬤";
+ }, 800);
+ }, 1600);
+ }
+
+ let pipAnimationTriggered = preloaded; // Flag to track if the animation has been triggered
+ let githubPushAnimationTriggered = preloaded; // Flag to track if the animation has been triggered
+
+ function handlePipIntersection(entries, observer) {
+ entries.forEach(entry => {
+ if (entry.isIntersecting && !pipAnimationTriggered) {
+ pipAnimationTriggered = true; // Set the flag to true to prevent re-triggering
+ setTimeout(() => {
+ typeInPipText(codeElementsArray[0]);
+ }, 500);
+ observer.unobserve(entry.target); // Stop observing the element
+ }
+ });
+ }
+
+ function handleGithubPushIntersection(entries, observer) {
+ entries.forEach(entry => {
+ if (entry.isIntersecting && !githubPushAnimationTriggered) {
+ githubPushAnimationTriggered = true; // Set the flag to true to prevent re-triggering
+ typeInGithubPushText(codeElementsArray[1]);
+ observer.unobserve(entry.target); // Stop observing the element
+ }
+ });
+ }
+
+ const pipObserver = new IntersectionObserver(handlePipIntersection, {
+ root: document, // Use the document start as the root
+ threshold: 0.8 // Trigger when 10% of the element is visible
+ });
+ const githubPushObserver = new IntersectionObserver(handleGithubPushIntersection, {
+ root: document, // Use the document start as the root
+ threshold: 0.6 // Trigger when 10% of the element is visible
+ });
+
+ pipObserver.observe(codeElementsArray[0]);
+ githubPushObserver.observe(codeElementsArray[1]);
+}
+
+window.addEventListener('load', function() {
+ loadTerminalText();
+ loadIntro();
+ fadeInIntroRowTexts();
+ const observer = new MutationObserver(addIntroEventListeners);
+ observer.observe(document.body, { childList: true, subtree: true });
+});
\ No newline at end of file
diff --git a/docs/v1/styles/styles.css b/docs/v1/styles/styles.css
new file mode 100644
index 00000000..49e0151e
--- /dev/null
+++ b/docs/v1/styles/styles.css
@@ -0,0 +1,160 @@
+/* Terminal hero animation styling */
+
+.terminal-border {
+ border: 4px solid #D1E6ED;
+ background-color: #D1E6ED;
+ padding: 5px;
+ width: 80%;
+ margin-left: auto;
+ margin-right: auto;
+ border-radius: 16px;
+ padding: 0;
+ box-shadow: 2px 8px 8px rgba(0, 0, 0, 0.15);
+}
+
+.terminal-container {
+ border: 8px solid black;
+ border-radius: 12px;
+ background-color: #262626;
+ height: 16em;
+ font-family: var(--font-jetbrains-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ padding: 0em 0 0 1em;
+ font-size: 1.2em;
+ color: white;
+}
+
+#terminal-content {
+ display: flex;
+ flex-direction: column;
+ justify-content: left;
+ gap: 0;
+}
+
+#terminal-content > * {
+ margin: 0;
+ padding: 0;
+}
+
+.cursor-bar {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ font-weight: bold;
+}
+
+#inline-code-img {
+ width: 1.2em;
+ height: auto;
+ opacity: 0;
+ display: none;
+ margin: 0;
+ padding: 0;
+ border-radius: 6px;
+}
+
+#inline-code-img.on {
+ opacity: 1;
+ display: flex;
+}
+
+.empty-space {
+ opacity: 0;
+ display: flex;
+}
+
+#toggle-empty-space {
+ opacity: 0;
+ display: flex;
+}
+
+.off {
+ display: none !important;
+}
+
+#barely-space {
+ opacity: 0;
+ font-size: 0.5em;
+ display: flex;
+}
+
+/* Heart rate monitor animations */
+
+.heartbeat-button {
+ position: relative;
+ padding: 0.4em 1.4em;
+ font-size: 1.6em;
+ font-weight: bold;
+ border: none;
+ color: white;
+ background: #7046E0;
+ cursor: pointer;
+ overflow: hidden;
+ border-radius: 8px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin: 0 auto;
+ margin-top: 2em;
+ margin-bottom: 1em;
+ box-shadow: 0 4px 6px rgba(112, 70, 224, 0.5); /* Purple shaded box shadow */
+ z-index: 2;
+ width: 30%;
+}
+
+/* API key button spacing */
+
+.api-key-button {
+ position: absolute;
+ background-color: #7046E0;
+ border: none;
+ color: white;
+ padding: 0.3em 0.8em;
+ font-size: 0.95em;
+ font-weight: bold;
+ border-radius: 4px;
+}
+
+.api-key-button.pattern2 {
+ margin-top: 2.5em; /* Set distance from the top of the div */
+ margin-left: 24.8em;
+}
+
+.api-key-button.pattern1 {
+ margin-top: 0.9em; /* Set distance from the top of the div */
+ margin-left: 19.2em;
+}
+
+.api-key-button.pattern0 {
+ margin-top: 4em; /* Set distance from the top of the div */
+ margin-left: 1.9em;
+}
+
+/* Discord CTA styling */
+.discord-cta-div {
+ display: flex;
+ margin: 0 auto;
+ align-items: center;
+ background-color: rgba(114, 137, 218, 0.6);
+ color: white;
+ border: 3px solid rgba(88, 100, 242, 0.8);
+ padding: 0em 1em;
+ border-radius: 8px;
+ margin-bottom: 1em;
+ width: 50%;
+ font-size: 1.1em;
+}
+
+.discord-logo {
+ width: 60%;
+ margin: 0em 0.25em;
+ height: auto;
+ border-radius: 8px;
+ padding: 0;
+}
+
+.discord-cta-text {
+ width: 900%;
+ text-align: center;
+ padding: 0;
+ margin: 0;
+}
\ No newline at end of file
diff --git a/docs/v1/usage/dashboard-info.mdx b/docs/v1/usage/dashboard-info.mdx
new file mode 100644
index 00000000..57001693
--- /dev/null
+++ b/docs/v1/usage/dashboard-info.mdx
@@ -0,0 +1,44 @@
+---
+title: "Dashboard"
+description: "Visualize your AgentOps analysis."
+mode: "wide"
+---
+
+## Insights Dashboard
+
+You need better insights to turn error-prone AI into stable workflows. Here's your new best friend.
+
+### Session Drilldown
+Here you will find a list of all of your previously recorded sessions and useful data about each such as total execution time.
+You also get helpful debugging info such as any SDK versions you were on if you're building on a supported agent framework like Crew or AutoGen.
+LLM calls are presented as a familiar chat history view, and charts give you a breakdown of the types of events that were called and how long they took.
+
+
+
+
+
+Find any past sessions from your Session Drawer.
+
+
+
+
+Most powerful of all is the Session Waterfall. On the left, a time visualization of all your LLM calls, Action events, Tool calls, and Errors.
+On the right, specific details about the event you've selected on the waterfall. For instance the exact prompt and completion for a given LLM call.
+Most of which has been automatically recorded for you.
+
+
+
+
+
+
+### Session Overview
+View a meta-analysis of all of your sessions in a single view.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/usage/environment-variables.mdx b/docs/v1/usage/environment-variables.mdx
index 5b724a0d..7389f9dc 100644
--- a/docs/v1/usage/environment-variables.mdx
+++ b/docs/v1/usage/environment-variables.mdx
@@ -5,9 +5,13 @@ mode: "wide"
In AgentOps fashion, you only need to add one line of "code" to your `.env` file 😊
-```python .env
-AGENTOPS_API_KEY=
-```
+
+
+ ```python .env
+ AGENTOPS_API_KEY=
+ ```
+
+
Find your AgentOps API Key in your Settings > [Projects & API Keys](https://app.agentops.ai/settings/projects) page.
@@ -27,4 +31,7 @@ AGENTOPS_ENV_DATA_OPT_OUT=FALSE
```
-
\ No newline at end of file
+
+
+
+
diff --git a/docs/v1/usage/langchain-callback-handler.mdx b/docs/v1/usage/langchain-callback-handler.mdx
index 2e719d06..9b36644d 100644
--- a/docs/v1/usage/langchain-callback-handler.mdx
+++ b/docs/v1/usage/langchain-callback-handler.mdx
@@ -68,4 +68,7 @@ If your project uses Langchain for Agents, Events and Tools, it may be easier to
If your project uses models with Langchain that are not yet supported by AgentOps, they can be supported by the Handler.
-
\ No newline at end of file
+
+
+
+
diff --git a/docs/v1/usage/recording-events.mdx b/docs/v1/usage/recording-events.mdx
index abbdf8af..2eb17f49 100644
--- a/docs/v1/usage/recording-events.mdx
+++ b/docs/v1/usage/recording-events.mdx
@@ -35,4 +35,7 @@ record(ActionEvent("event_type1"))
```
-
\ No newline at end of file
+
+
+
+
diff --git a/docs/v1/usage/sdk-reference.mdx b/docs/v1/usage/sdk-reference.mdx
index 6b1e9008..647ba101 100644
--- a/docs/v1/usage/sdk-reference.mdx
+++ b/docs/v1/usage/sdk-reference.mdx
@@ -154,4 +154,7 @@ This callback handler is intended to be used as an option in place of AgentOps a
when using Langchain as your LLM calling library.
-
\ No newline at end of file
+
+
+
+
diff --git a/docs/v1/usage/tracking-agents.mdx b/docs/v1/usage/tracking-agents.mdx
index 80fab27c..04c4ec41 100644
--- a/docs/v1/usage/tracking-agents.mdx
+++ b/docs/v1/usage/tracking-agents.mdx
@@ -24,6 +24,7 @@ If omitted, agent name defaults to the name of the class (e.g. MyAgent).
-
\ No newline at end of file
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/usage/tracking-llm-calls.mdx b/docs/v1/usage/tracking-llm-calls.mdx
index 98bc9c72..d51137ac 100644
--- a/docs/v1/usage/tracking-llm-calls.mdx
+++ b/docs/v1/usage/tracking-llm-calls.mdx
@@ -29,4 +29,7 @@ To get started, just follow the quick start guide.
-
\ No newline at end of file
+
+
+
+