Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix parsing of Infobip whatsapp API schema #516

Merged
merged 20 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .devcontainer/devcontainer.env
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,3 @@ BING_API_KEY=${BING_API_KEY}

# GIPHY key
GIPHY_API_KEY=${GIPHY_API_KEY}

# POSTMAN key
POSTMAN_API_KEY=${POSTMAN_API_KEY}
3 changes: 0 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@
},
"BING_API_KEY": {
"description": "This key is optional. The WebSurfer agent can work without it, but when added, it uses Bing's search and data services to improve information retrieval. You can always set it later as an environment variable in the codespace terminal."
},
"POSTMAN_API_KEY": {
"description": "This key is optional and only needed if you are testing OpenAPI code. Leave it blank if not required. You can always set it later as an environment variable in the codespace terminal."
}
},
"shutdownAction": "stopCompose",
Expand Down
3 changes: 0 additions & 3 deletions .devcontainer/python-3.11/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@
},
"BING_API_KEY": {
"description": "This key is optional. The WebSurfer agent can work without it, but when added, it uses Bing's search and data services to improve information retrieval. You can always set it later as an environment variable in the codespace terminal."
},
"POSTMAN_API_KEY": {
"description": "This key is optional and only needed if you are testing OpenAPI code. Leave it blank if not required. You can always set it later as an environment variable in the codespace terminal."
}
},
"shutdownAction": "stopCompose",
Expand Down
3 changes: 0 additions & 3 deletions .devcontainer/python-3.12/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@
},
"BING_API_KEY": {
"description": "This key is optional. The WebSurfer agent can work without it, but when added, it uses Bing's search and data services to improve information retrieval. You can always set it later as an environment variable in the codespace terminal."
},
"POSTMAN_API_KEY": {
"description": "This key is optional and only needed if you are testing OpenAPI code. Leave it blank if not required. You can always set it later as an environment variable in the codespace terminal."
}
},
"shutdownAction": "stopCompose",
Expand Down
3 changes: 0 additions & 3 deletions .devcontainer/python-3.9/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@
},
"BING_API_KEY": {
"description": "This key is optional. The WebSurfer agent can work without it, but when added, it uses Bing's search and data services to improve information retrieval. You can always set it later as an environment variable in the codespace terminal."
},
"POSTMAN_API_KEY": {
"description": "This key is optional and only needed if you are testing OpenAPI code. Leave it blank if not required. You can always set it later as an environment variable in the codespace terminal."
}
},
"shutdownAction": "stopCompose",
Expand Down
12 changes: 2 additions & 10 deletions .github/workflows/pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,6 @@ jobs:
use-llms: "llm"
secrets: inherit # pragma: allowlist secret

test-with-postman:
uses: ./.github/workflows/test.yaml
with:
python-version: "3.9"
environment: testing
use-postman: true
secrets: inherit # pragma: allowlist secret

test-macos-latest:
if: github.event.pull_request.draft == false
runs-on: macos-latest
Expand All @@ -121,7 +113,7 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true'
run: pip install .[submodules,docs,testing]
- name: Test
run: bash scripts/test.sh -m "not (nats or anthropic or azure_oai or openai or togetherai or llm or postman)"
run: bash scripts/test.sh -m "not (nats or anthropic or azure_oai or openai or togetherai or llm)"

test-windows-latest:
if: github.event.pull_request.draft == false
Expand All @@ -139,7 +131,7 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true'
run: pip install .[submodules,docs,testing]
- name: Test
run: bash scripts/test.sh -m "not (nats or anthropic or azure_oai or openai or togetherai or llm or postman)"
run: bash scripts/test.sh -m "not (nats or anthropic or azure_oai or openai or togetherai or llm)"

coverage-combine:
needs:
Expand Down
26 changes: 6 additions & 20 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ on:
required: false
type: string
default: ""
use-postman:
description: 'Use Postman API in the tests'
required: false
type: boolean
default: false

jobs:
test:
Expand Down Expand Up @@ -75,9 +70,6 @@ jobs:
if [ -n "${{ secrets.BING_API_KEY }}" ]; then
echo "BING_API_KEY=${{ secrets.BING_API_KEY }}" >> $GITHUB_ENV
fi
if [ -n "${{ secrets.POSTMAN_API_KEY }}" ]; then
echo "POSTMAN_API_KEY=${{ secrets.POSTMAN_API_KEY }}" >> $GITHUB_ENV
fi

- uses: actions/checkout@v4
- name: Set up Python
Expand Down Expand Up @@ -105,23 +97,17 @@ jobs:
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Test without LLMs
if: ${{ inputs.use-llms == '' && !inputs.use-postman }}
run: bash scripts/test.sh -vv -m "not (anthropic or azure_oai or openai or togetherai or llm or postman)"
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}-${{ inputs.use-postman }}
CONTEXT: ${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}-${{ inputs.use-postman }}
- name: Test with Postman
if: ${{ inputs.use-postman }}
run: bash scripts/test.sh -vv -m postman
if: ${{ inputs.use-llms == ''}}
run: bash scripts/test.sh -vv -m "not (anthropic or azure_oai or openai or togetherai or llm)"
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}-${{ inputs.use-postman }}
CONTEXT: ${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}-${{ inputs.use-postman }}
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}
CONTEXT: ${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}
- name: Test with LLMs
if: ${{ inputs.use-llms != '' }}
run: bash scripts/test.sh -vv -m "${{ inputs.use-llms }}"
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}-${{ inputs.use-postman }}
CONTEXT: ${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}-${{ inputs.use-postman }}
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}
CONTEXT: ${{ runner.os }}-py${{ inputs.python-version }}-${{ inputs.use-llms }}
- name: Run Playwright tests without LLMs
if: ${{ inputs.python-version != '3.9' }}
run: npx playwright test -c "playwright.llm-sans.config.ts"
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ search:
- fastapi_code_generator_helpers
- [ArgumentWithDescription](api/fastagency/api/openapi/fastapi_code_generator_helpers/ArgumentWithDescription.md)
- [patch_get_parameter_type](api/fastagency/api/openapi/fastapi_code_generator_helpers/patch_get_parameter_type.md)
- patch_datamodel_code_generator
- [patch_apply_discriminator_type](api/fastagency/api/openapi/patch_datamodel_code_generator/patch_apply_discriminator_type.md)
- patch_fastapi_code_generator
- [patch_function_name_parsing](api/fastagency/api/openapi/patch_fastapi_code_generator/patch_function_name_parsing.md)
- [patch_generate_code](api/fastagency/api/openapi/patch_fastapi_code_generator/patch_generate_code.md)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
# 0.5 - API
# 2 - Release
# 3 - Contributing
# 5 - Template Page
# 10 - Default
search:
boost: 0.5
---

::: fastagency.api.openapi.patch_datamodel_code_generator.patch_apply_discriminator_type
21 changes: 11 additions & 10 deletions docs/docs/en/tutorials/whatsapp/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ In this tutorial, we will explore how to leverage the **FastAgency** framework t

1. [**`WebSurferAgent`**](../../api/fastagency/runtimes/autogen/agents/websurfer/WebSurferAgent.md): A web-scraping agent capable of retrieving relevant content from webpages (learn more [here](../../user-guide/runtimes/autogen/websurfer.md)).

2. **WhatsApp agent** – An agent that interacts with the [Infobip WhatsApp API](https://www.infobip.com/docs/api/channels/whatsapp){target="_blank"} to send WhatsApp messages based on the user’s request. It will be created using the standard [**`ConversableAgent`**](https://microsoft.github.io/autogen/0.2/docs/reference/agentchat/conversable_agent/){target="_blank"} from [AutoGen](https://microsoft.github.io/autogen){target="_blank"} and the [**`OpenAPI`**](../../api/fastagency/api/openapi/OpenAPI.md) object instantiated with an OpenAPI [specification](https://raw.githubusercontent.com/airtai/fastagency/refs/heads/main/examples/openapi/whatsapp_openapi.json){target="_blank"} of Infobip's [REST API](https://www.infobip.com/docs/api/channels/whatsapp){target="_blank"}.
2. **WhatsApp agent** – An agent that interacts with the [Infobip WhatsApp API](https://www.infobip.com/docs/api/channels/whatsapp){target="_blank"} to send WhatsApp messages based on the user’s request. It will be created using the standard [**`ConversableAgent`**](https://microsoft.github.io/autogen/0.2/docs/reference/agentchat/conversable_agent/){target="_blank"} from [AutoGen](https://microsoft.github.io/autogen){target="_blank"} and the [**`OpenAPI`**](../../api/fastagency/api/openapi/OpenAPI.md) object instantiated with an OpenAPI [specification](https://dev.infobip.com/openapi/products/whatsapp.json){target="_blank"} of Infobip's [REST API](https://www.infobip.com/docs/api/channels/whatsapp){target="_blank"}.

The chat system will operate between these two agents and the user, allowing them to scrape web content and send the relevant information via WhatsApp, all within a seamless conversation. This tutorial will guide you through setting up these agents, handling user interaction, and ensuring secure API communication.

Expand Down Expand Up @@ -106,11 +106,11 @@ You can set the API keys in your terminal as an environment variable:
Now we will go over each key part of the code, explaining its function and purpose within the FastAgency framework. Understanding these components is crucial for building a dynamic interaction between the user, the [**`WebSurferAgent`**](../../api/fastagency/runtimes/autogen/agents/websurfer/WebSurferAgent.md), and the **WhatsAppAgent**.

### Creating the WhatsApp API Instance
The following lines shows hot to initializes the WhatsApp API by loading the OpenAPI specification from a URL. The OpenAPI spec defines how to interact with the WhatsApp API, including endpoints, parameters, and security details.
The following lines shows how to initializes the WhatsApp API by loading the OpenAPI specification from a URL. The OpenAPI spec defines how to interact with the WhatsApp API, including endpoints, parameters, and security details.

Also, we configure the **WhatsApp API** with the __*WHATSAPP_API_KEY*__ using __*set_security_params*__ to authenticate our requests.
```python
{! docs_src/tutorials/whatsapp/main.py [ln:24-31] !}
{! docs_src/tutorials/whatsapp/main.py [ln:24-34] !}
```

For more information, visit [**API Integration User Guide**](../../user-guide/api/index.md){target="_blank"}.
Expand All @@ -121,14 +121,15 @@ For more information, visit [**API Integration User Guide**](../../user-guide/ap
Here, we initialize a new workflow using ***AutoGenWorkflows()*** and register it under the name ***"whatsapp_and_websurfer"***. The ***@wf.register*** decorator registers the function to handle chat flow with security enabled, combining both WhatsAppAgent and WebSurferAgent.

```python
{! docs_src/tutorials/whatsapp/main.py [ln:60-62] !}
{! docs_src/tutorials/whatsapp/main.py [ln:61-65] !}
...
```

### Interaction with the user
This is a core function used by the **WhatsAppAgent** to either present the task result or ask a follow-up question to the user. The message is wrapped in a ***TextInput*** object, and then ***ui.process_message()*** sends it for user interaction.

```python
{! docs_src/tutorials/whatsapp/main.py [ln:65-76] !}
{! docs_src/tutorials/whatsapp/main.py [ln:69-79] !}
```

### Creating the WhatsApp and WebSurfer Agents
Expand All @@ -137,7 +138,7 @@ This is a core function used by the **WhatsAppAgent** to either present the task
- [**`WebSurferAgent`**](../../api/fastagency/runtimes/autogen/agents/websurfer/WebSurferAgent.md): The [**`WebSurferAgent`**](../../api/fastagency/runtimes/autogen/agents/websurfer/WebSurferAgent.md) is responsible for scraping web content and passes the retrieved data to the **WhatsAppAgent**. It’s configured with a summarizer to condense web content, which is useful when presenting concise data to the user. For more information, visit [**WebSurfer User Guide**](../../user-guide/runtimes/autogen/websurfer.md).

```python
{! docs_src/tutorials/whatsapp/main.py [ln:77-94] !}
{! docs_src/tutorials/whatsapp/main.py [ln:81-97] !}
```


Expand All @@ -146,13 +147,13 @@ This is a core function used by the **WhatsAppAgent** to either present the task
The function ***present_completed_task_or_ask_question*** is registered to allow the **WhatsAppAgent** to ask questions or present completed tasks after receiving data from the [**`WebSurferAgent`**](../../api/fastagency/runtimes/autogen/agents/websurfer/WebSurferAgent.md).

```python
{! docs_src/tutorials/whatsapp/main.py [ln:95-103] !}
{! docs_src/tutorials/whatsapp/main.py [ln:99-106] !}
```


We register the WhatsApp API, which allows the **WhatsAppAgent** to handle tasks like suggesting messages that will be sent to the user.
```python
{! docs_src/tutorials/whatsapp/main.py [ln:105-109] !}
{! docs_src/tutorials/whatsapp/main.py [ln:108-113] !}
```

### Initiating the Chat
Expand All @@ -162,15 +163,15 @@ We initiate the conversation between the user, [**`WebSurferAgent`**](../../api/
Once the conversation ends, the summary is returned to the user, wrapping up the session.

```python
{! docs_src/tutorials/whatsapp/main.py [ln:117-124] !}
{! docs_src/tutorials/whatsapp/main.py [ln:121-126] !}
```

### Starting the Application

The FastAgency app is created, using the registered workflows (**`wf`**) and web-based user interface ([**`MesopUI`**](../../api/fastagency/ui/mesop/MesopUI.md)). This makes the conversation between agents and the user interactive.

```python
{! docs_src/tutorials/whatsapp/main.py [ln:127] !}
{! docs_src/tutorials/whatsapp/main.py [ln:131] !}
```

For more information, visit [**Mesop User Guide**](../../user-guide/ui/mesop/basics.md){target="_blank"}.
Expand Down
10 changes: 7 additions & 3 deletions docs/docs_src/tutorials/whatsapp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from fastagency import UI, FastAgency
from fastagency.api.openapi.client import OpenAPI
from fastagency.api.openapi.security import APIKeyHeader
from fastagency.runtimes.autogen.agents.websurfer import WebSurferAgent
from fastagency.runtimes.autogen import AutoGenWorkflows
from fastagency.runtimes.autogen.agents.websurfer import WebSurferAgent
from fastagency.ui.mesop import MesopUI

llm_config = {
Expand All @@ -21,9 +21,12 @@
"temperature": 0.8,
}

openapi_url = "https://raw.githubusercontent.com/airtai/fastagency/refs/heads/main/examples/openapi/whatsapp_openapi.json"
openapi_url = "https://dev.infobip.com/openapi/products/whatsapp.json"

whatsapp_api = OpenAPI.create(
openapi_url=openapi_url,
# this is an optional parameter, but specified here because servers are not specified in the OpenAPI specification
servers=[{"url": "https://api.infobip.com"}],
sternakt marked this conversation as resolved.
Show resolved Hide resolved
)

header_authorization = "App " # pragma: allowlist secret
Expand Down Expand Up @@ -90,7 +93,7 @@ def present_completed_task_or_ask_question(
human_input_mode="NEVER",
executor=whatsapp_agent,
is_termination_msg=is_termination_msg,
bing_api_key=os.getenv("BING_API_KEY")
bing_api_key=os.getenv("BING_API_KEY"),
)

register_function(
Expand All @@ -106,6 +109,7 @@ def present_completed_task_or_ask_question(
api=whatsapp_api,
callers=whatsapp_agent,
executors=web_surfer,
functions=["send_whatsapp_text_message"],
)

initial_message = ui.text_input(
Expand Down
Loading
Loading