Skip to content

Commit

Permalink
Update FastAPI and NATS.io adapter sections with custom HTML/JS clien…
Browse files Browse the repository at this point in the history
…t implementation (#461)

* WIP

* WIP

* Add custom client section to FastAPI + Nats adapter docs

* wip

* WIP

---------

Co-authored-by: Davor Runje <[email protected]>
  • Loading branch information
harishmohanraj and davorrunje authored Oct 23, 2024
1 parent aac94b6 commit 98bef19
Show file tree
Hide file tree
Showing 9 changed files with 453 additions and 182 deletions.
4 changes: 2 additions & 2 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@
"filename": "docs/docs/en/user-guide/adapters/fastapi_nats/index.md",
"hashed_secret": "3be2e9dd1980856faddf5bd205e3ff96b24776aa",
"is_verified": false,
"line_number": 173,
"line_number": 287,
"is_secret": false
}
],
Expand All @@ -178,7 +178,7 @@
"filename": "docs/docs/en/user-guide/adapters/nats/index.md",
"hashed_secret": "3be2e9dd1980856faddf5bd205e3ff96b24776aa",
"is_verified": false,
"line_number": 144,
"line_number": 140,
"is_secret": false
}
],
Expand Down
120 changes: 60 additions & 60 deletions docs/docs/en/user-guide/adapters/fastapi/index.md

Large diffs are not rendered by default.

369 changes: 261 additions & 108 deletions docs/docs/en/user-guide/adapters/fastapi_nats/index.md

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/docs/en/user-guide/adapters/images/mesop_fastapi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 3 additions & 7 deletions docs/docs/en/user-guide/adapters/nats/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,11 @@ The **Nats Adapter** in FastAgency enables seamless integration of your workflow

## Use Cases

This section outlines the scenarios where it's particularly beneficial to use the **Nats** Adapter.
When to Use the Nats Adapter:

### When to Use the Nats Adapter
- **High User Demand**: If you need to scale above what can be achieved with multiple-workers of the [**FastAPI** adapter](../fastapi/index.md), then you can use [**Nats.io MQ**](https://nats.io/){target="_blank"}-based message que and multiple workers consuming and producing messages. This kind of distributed, [message-queue architecture](https://en.wikipedia.org/wiki/Message_queue){target="_blank"} allows you to scale up not just across multiple workers, but also across multiple machines and even across multiple clusters.

- **Default Client Application**: If you prefer using the **default Mesop client** provided by FastAgency without the need to build your own client application.

- **High User Demand**: When your application requires **high scalability** to handle a large number of users or messages, and you are comfortable with a more complex production setup involving a message broker. For example, it's well-suited for building a **scalable chat application** for a larger company or external customers.

- **Conversation Auditing**: If you need the ability to **audit conversations**, the Nats Adapter provides the necessary infrastructure to enable this feature.
- **Observability**: If you need the ability to **audit workflow executions** both in realtime and posteriori, the Nats Adapter provides the necessary infrastructure to enable this feature.

## Architecture Overview

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,4 @@ async def get() -> HTMLResponse:


# start the provider with the following command
# uvicorn main_custom_fastapi_client:app --host 0.0.0.0 --port 8008 --reload
# uvicorn main_fastapi_custom_client:app --host 0.0.0.0 --port 8008 --reload
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
from os import environ

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

from fastagency.adapters.fastapi import FastAPIAdapter
from fastagency.adapters.nats import NatsAdapter

html = """
<!DOCTYPE html>
<html>
<head>
<title>FastAgency Chat App</title>
</head>
<body>
<h1>FastAgency Chat App</h1>
<div id="workflows"></div>
<ul id="messages"></ul>
<script>
const API_URL = 'http://0.0.0.0:8008/fastagency';
const WS_URL = 'ws://0.0.0.0:8008/fastagency/ws'; // nosemgrep
let socket;
async function fetchWorkflows() {
const response = await fetch(`${API_URL}/discovery`);
const workflows = await response.json();
const container = document.getElementById('workflows');
workflows.forEach(workflow => {
const button = document.createElement('button');
button.textContent = workflow.description;
button.onclick = () => startWorkflow(workflow.name);
container.appendChild(button);
});
}
async function startWorkflow(name) {
const payload = {
workflow_name: name,
workflow_uuid: generateUUID(),
user_id: null, // Set to null for single-user applications; otherwise, provide the appropriate user ID
params: {}
};
const response = await fetch(`${API_URL}/initiate_workflow`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const workflowJson = await response.json();
connectWebSocket(workflowJson);
}
function connectWebSocket(workflowJson) {
socket = new WebSocket(WS_URL);
socket.onopen = () => {
const initMessage = {
name: workflowJson.name,
workflow_uuid: workflowJson.workflow_uuid,
user_id: workflowJson.user_id,
params: {}
};
socket.send(JSON.stringify(initMessage));
};
socket.onmessage = (event) => handleMessage(JSON.parse(event.data));
}
function handleMessage(message) {
const messagesList = document.getElementById('messages');
const li = document.createElement('li');
if (message.type === 'text_input') {
const response = prompt(message.content.prompt);
socket.send(response);
li.textContent = `${message.sender} -> ${message.recipient}: ${message.content.prompt}`;
} else {
li.textContent = `${message.sender} -> ${message.recipient}: ${message.content?.body || message?.type || JSON.stringify(message)}`;
}
messagesList.appendChild(li);
}
fetchWorkflows();
// Helper function for generating UUID
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
if (c === 'x') {
return (Math.random() * 16 | 0).toString(16);
} else {
return (Math.random() * 16 | 0 & 0x3 | 0x8).toString(16);
}
});
}
</script>
</body>
</html>
"""

nats_url = environ.get("NATS_URL", "nats://localhost:4222")
nats_user: str = "fastagency"
nats_password: str = environ.get("FASTAGENCY_NATS_PASSWORD", "fastagency_nats_password") # type: ignore[assignment]

provider = NatsAdapter.create_provider(
nats_url=nats_url, user=nats_user, password=nats_password
)

adapter = FastAPIAdapter(
provider=provider,
)

# app = FastAPI(lifespan=provider.lifespan)
app = FastAPI()
app.include_router(adapter.router)


@app.get("/")
async def get() -> HTMLResponse:
return HTMLResponse(html)


# start the provider with the following command
# uvicorn main_2_fastapi_custom_client:app --host 0.0.0.0 --port 8008 --reload

0 comments on commit 98bef19

Please sign in to comment.