diff --git a/.github/scripts/run_single_example.sh b/.github/scripts/run_single_example.sh index 6905ae8301..8d9d588e40 100755 --- a/.github/scripts/run_single_example.sh +++ b/.github/scripts/run_single_example.sh @@ -63,6 +63,50 @@ patch_dependencies() { mv "$tmp_file" "$file" } +is_server_example() { + grep -q "^[^#]*RagbitsAPI" "$1" +} + +run_server_example() { + local file="$1" + local port=8000 + local max_wait=60 + local pid + + echo "Starting server example: $file" + uv run "$file" & + pid=$! + trap 'kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null' EXIT + + echo "Waiting for server to start (PID: $pid, port: $port)..." + local elapsed=0 + while [[ $elapsed -lt $max_wait ]]; do + if ! kill -0 "$pid" 2>/dev/null; then + echo "Server process exited unexpectedly" + wait "$pid" + trap - EXIT + return $? + fi + + if curl -sf "http://127.0.0.1:$port/api/config" > /dev/null 2>&1; then + echo "Server started successfully after ${elapsed}s" + kill "$pid" 2>/dev/null + wait "$pid" 2>/dev/null || true + trap - EXIT + return 0 + fi + + sleep 2 + elapsed=$((elapsed + 2)) + done + + echo "Server failed to start within ${max_wait}s" + kill "$pid" 2>/dev/null + wait "$pid" 2>/dev/null || true + trap - EXIT + return 1 +} + if ! has_script_section "$EXAMPLE_FILE"; then echo "Skipping $EXAMPLE_FILE (no script section found)" exit 0 @@ -71,15 +115,24 @@ fi echo "Running the script: $EXAMPLE_FILE" patch_dependencies "$EXAMPLE_FILE" "$PR_BRANCH" -set +e -timeout 120s uv run "$EXAMPLE_FILE" -exit_code=$? -set -e +if is_server_example "$EXAMPLE_FILE"; then + set +e + run_server_example "$EXAMPLE_FILE" + exit_code=$? + set -e +else + set +e + timeout 120s uv run "$EXAMPLE_FILE" + exit_code=$? + set -e + + if [[ $exit_code -eq 124 ]]; then + echo "Script timed out after 120 seconds" + exit 1 + fi +fi -if [[ $exit_code -eq 124 ]]; then - echo "Script timed out after 120 seconds" - exit 1 -elif [[ $exit_code -ne 0 ]]; then +if [[ $exit_code -ne 0 ]]; then echo "Script failed" exit $exit_code else diff --git a/.github/workflows/main-checks.yml b/.github/workflows/main-checks.yml deleted file mode 100644 index b557795780..0000000000 --- a/.github/workflows/main-checks.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Main branch checks - -on: - push: - branches: - - main - -jobs: - check-changelog-update: - name: Check changelog update - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Verify changelog updates - run: | - ./.github/scripts/check_changelog_update.sh - - check-source-changes: - name: Check source changes - runs-on: ubuntu-latest - outputs: - packages-changed: ${{ steps.filter.outputs.packages-changed }} - ui-changed: ${{ steps.filter.outputs.ui-changed }} - client-changed: ${{ steps.filter.outputs.client-changed }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check for changes - id: filter - run: | - ./.github/scripts/check_source_changes.sh ${{ github.event.before }} ${{ github.sha }} - - packages: - needs: check-source-changes - if: ${{ needs.check-source-changes.outputs.packages-changed == 'true' }} - uses: ./.github/workflows/shared-packages.yml - secrets: inherit - - ui: - needs: check-source-changes - if: ${{ needs.check-source-changes.outputs.ui-changed == 'true' }} - uses: ./.github/workflows/shared-ui.yml diff --git a/.github/workflows/pull-request-checks.yml b/.github/workflows/pull-request-checks.yml index 41b8e87e6c..b9ff7de9cb 100644 --- a/.github/workflows/pull-request-checks.yml +++ b/.github/workflows/pull-request-checks.yml @@ -15,7 +15,7 @@ jobs: - name: Verify changelog updates run: | - ./.github/scripts/check_changelog_update.sh + ./.github/scripts/check_changelog_update.sh ${{ github.event.pull_request.base.ref }} check-pr-title: name: Check pull request title diff --git a/examples/chat/authenticated_chat.py b/examples/chat/authenticated_chat.py index f72717f314..c5bd0b6b42 100644 --- a/examples/chat/authenticated_chat.py +++ b/examples/chat/authenticated_chat.py @@ -373,3 +373,9 @@ def get_multi_auth_backend() -> MultiAuthenticationBackend: get_discord_auth_backend(), # Discord OAuth2 authentication ] ) + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(MyAuthenticatedChat, auth_backend=get_auth_backend()).run() diff --git a/examples/chat/chat.py b/examples/chat/chat.py index 314e8e854e..48ce13cffc 100644 --- a/examples/chat/chat.py +++ b/examples/chat/chat.py @@ -17,7 +17,6 @@ # "ragbits-chat", # ] # /// -# import asyncio import uuid @@ -224,3 +223,9 @@ async def chat( yield self.create_usage_response(streaming_result.usage) yield self.create_followup_messages(["Example Response 1", "Example Response 2", "Example Response 3"]) + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(MyChat).run() diff --git a/examples/chat/code_planner.py b/examples/chat/code_planner.py index 523e7a77f9..bdb0366fc2 100644 --- a/examples/chat/code_planner.py +++ b/examples/chat/code_planner.py @@ -145,3 +145,9 @@ async def chat( description=self.planning_state.plan.last_completed_task.result, ) yield self.create_plan_item_response(self.planning_state.plan.last_completed_task) + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(CodePlannerChat).run() diff --git a/examples/chat/file_explorer_agent.py b/examples/chat/file_explorer_agent.py index 450eadbfd6..786d245053 100644 --- a/examples/chat/file_explorer_agent.py +++ b/examples/chat/file_explorer_agent.py @@ -629,3 +629,9 @@ async def chat( # noqa: PLR0912 case Usage(): # Usage information yield self.create_usage_response(response) + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(FileExplorerChat).run() diff --git a/examples/chat/no_summary_generator.py b/examples/chat/no_summary_generator.py index 7a3d9eb8a7..0b86194b42 100644 --- a/examples/chat/no_summary_generator.py +++ b/examples/chat/no_summary_generator.py @@ -39,3 +39,9 @@ async def chat( streaming_result = self.llm.generate_streaming([*history, {"role": "user", "content": message}]) async for chunk in streaming_result: yield self.create_text_response(chunk) + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(MyChat).run() diff --git a/examples/chat/offline_chat.py b/examples/chat/offline_chat.py index c5560b142f..473392683c 100644 --- a/examples/chat/offline_chat.py +++ b/examples/chat/offline_chat.py @@ -173,3 +173,9 @@ async def upload_handler(self, file: UploadFile) -> None: # noqa: PLR6301 # If you want to notify the user in the chat, the user would usually send a message # mentioning they uploaded a file, or you could potentially trigger something else. # Currently the flow is: UI uploads -> Backend handles -> UI gets 200 OK. + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(MyChat).run() diff --git a/examples/chat/stream_events_from_tools_to_chat.py b/examples/chat/stream_events_from_tools_to_chat.py index fa74d2dde6..e811697def 100644 --- a/examples/chat/stream_events_from_tools_to_chat.py +++ b/examples/chat/stream_events_from_tools_to_chat.py @@ -8,7 +8,7 @@ To run the API, use the following command: ```bash - uvicorn examples.chat.stream_events_from_tools_to_chat:app + ragbits api run examples.chat.stream_events_from_tools_to_chat:RevenueChatInterface ``` """ @@ -110,4 +110,5 @@ async def chat( yield chunk -app = RagbitsAPI(RevenueChatInterface).app +if __name__ == "__main__": + RagbitsAPI(RevenueChatInterface).run() diff --git a/examples/chat/themed_chat.py b/examples/chat/themed_chat.py index 166b48bb0b..432eef3c14 100644 --- a/examples/chat/themed_chat.py +++ b/examples/chat/themed_chat.py @@ -54,3 +54,9 @@ async def chat(self, message: str, history: list[dict], context: ChatContext) -> response = await self.llm.generate(prompt) yield ChatInterface.create_text_response(str(response)) + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(MyChat).run() diff --git a/examples/chat/tutorial.py b/examples/chat/tutorial.py index e406c5fcd0..2083cbaaea 100644 --- a/examples/chat/tutorial.py +++ b/examples/chat/tutorial.py @@ -31,7 +31,6 @@ # "ragbits-agents" # ] # /// -# import base64 from collections.abc import AsyncGenerator @@ -43,6 +42,7 @@ from ragbits.agents import Agent, ToolCallResult from ragbits.agents.tools.openai import get_image_generation_tool, get_web_search_tool from ragbits.chat.auth import ListAuthenticationBackend +from ragbits.chat.auth.session_store import InMemorySessionStore from ragbits.chat.interface import ChatInterface from ragbits.chat.interface.forms import FeedbackConfig, UserSettings from ragbits.chat.interface.types import ChatContext, ChatResponse, LiveUpdateType @@ -259,4 +259,10 @@ def get_auth_backend() -> ListAuthenticationBackend: }, ] - return ListAuthenticationBackend(users) + return ListAuthenticationBackend(users, session_store=InMemorySessionStore()) + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(MyChat, auth_backend=get_auth_backend()).run() diff --git a/examples/chat/upload_chat.py b/examples/chat/upload_chat.py index 8cc2c54ee5..2faf95c001 100644 --- a/examples/chat/upload_chat.py +++ b/examples/chat/upload_chat.py @@ -72,3 +72,9 @@ async def upload_handler(self, file: UploadFile) -> None: # noqa: PLR6301 # If you want to notify the user in the chat, the user would usually send a message # mentioning they uploaded a file, or you could potentially trigger something else. # Currently the flow is: UI uploads -> Backend handles -> UI gets 200 OK. + + +if __name__ == "__main__": + from ragbits.chat.api import RagbitsAPI + + RagbitsAPI(UploadChat).run()