From 5b4e39ffdce8680ff5a67d2ed2c6d1778e8d8f7d Mon Sep 17 00:00:00 2001 From: liyaka Date: Sun, 15 Sep 2024 17:44:53 +0300 Subject: [PATCH 1/7] add docker compose and simple nginx confi for it --- .github/workflows/publish_helm_chart.yaml | 2 +- docker-compose.yaml | 112 ++++++++++++++++++++++ nginx_default_local.conf | 27 ++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 docker-compose.yaml create mode 100644 nginx_default_local.conf diff --git a/.github/workflows/publish_helm_chart.yaml b/.github/workflows/publish_helm_chart.yaml index 9b8ed76dd..0e71f7ef9 100644 --- a/.github/workflows/publish_helm_chart.yaml +++ b/.github/workflows/publish_helm_chart.yaml @@ -41,7 +41,7 @@ jobs: fetch-depth: 0 - name: Install Helm - uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3.5 + uses: azure/setup-helm@v4.2.0 with: version: v3.13.0 diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 000000000..31cc50d98 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,112 @@ +name: opik + +services: + mysql: + image: mysql:8.4.2 + hostname: mysql + environment: + MYSQL_ROOT_PASSWORD: opik + MYSQL_DATABASE: opik + MYSQL_USER: opik + MYSQL_PASSWORD: opik + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"] + timeout: 1s + interval: 1s + retries: 300 + ports: + - "3306:3306" + + redis: + image: redis:7.2.4-alpine3.19 + hostname: redis + command: redis-server --requirepass opik + ports: + - '6379:6379' + healthcheck: + test: [ "CMD", "nc", "-z", "localhost", "6379" ] + interval: 2s + timeout: 4s + retries: 20 + start_period: 30s + restart: always + + + clickhouse: + image: clickhouse/clickhouse-server:23.8.15.35-alpine + hostname: clickhouse + environment: + CLICKHOUSE_DB: opik + CLICKHOUSE_USER: opik + CLICKHOUSE_PASSWORD: opik + # Enables SQL-driven Access Control and Account Management: + # https://clickhouse.com/docs/en/operations/access-rights#enabling-access-control + CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1 + ports: + - "8123:8123" # HTTP default port + - "9000:9000" # Native Protocol port + volumes: # Mounted on your $HOME folder + - ~/clickhouse/data:/var/lib/clickhouse/ + - ~/clickhouse/logs:/var/log/clickhouse-server/ + healthcheck: + test: [ "CMD", "wget", "--spider", "-q", "http://127.0.0.1:8123/ping" ] + interval: 1s + timeout: 1s + retries: 300 + + backend: + image: opik-backend + hostname: backend + build: + context: ./apps/opik-backend + dockerfile: Dockerfile + args: + OPIK_VERSION: latest + command: ["bash", "-c", "./run_db_migrations.sh && ./entrypoint.sh"] + environment: + DOCKER_BUILDKIT: 1 + STATE_DB_URL: "jdbc:mysql://mysql:3306/opik?createDatabaseIfNotExist=true&rewriteBatchedStatements=true" + STATE_DB_DATABASE_NAME: opik + STATE_DB_USER: opik + STATE_DB_PASS: opik + ANALYTICS_DB_MIGRATIONS_URL: "jdbc:clickhouse://clickhouse:8123" + ANALYTICS_DB_MIGRATIONS_USER: opik + ANALYTICS_DB_MIGRATIONS_PASS: opik + ANALYTICS_DB_PROTOCOL: "HTTP" + ANALYTICS_DB_HOST: "clickhouse" + ANALYTICS_DB_PORT: 8123 + ANALYTICS_DB_USERNAME: opik + ANALYTICS_DB_DATABASE_NAME: opik + JAVA_OPTS: "-Dliquibase.propertySubstitutionEnabled=true" + REDIS_URL: redis://:opik@redis:6379/ + ANALYTICS_DB_PASS: opik + ports: + - "8080:8080" + - "3003:3003" + depends_on: + mysql: + condition: service_healthy + clickhouse: + condition: service_healthy + + frontend: + image: opik-frontend + hostname: frontend + build: + context: ./apps/opik-frontend + dockerfile: Dockerfile + args: + OPIK_VERSION: latest + ports: + - "5173:5173" + extra_hosts: + - "apihost:host-gateway" + volumes: + - ./nginx_default_local.conf:/etc/nginx/conf.d/default.conf + depends_on: + backend: + condition: service_started + + +networks: + default: \ No newline at end of file diff --git a/nginx_default_local.conf b/nginx_default_local.conf new file mode 100644 index 000000000..46175df8d --- /dev/null +++ b/nginx_default_local.conf @@ -0,0 +1,27 @@ +server { + listen 5173; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + location /api/ { + rewrite /api/(.*) /$1 break; + proxy_pass http://apihost:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_read_timeout 90; + proxy_connect_timeout 90; + proxy_send_timeout 90; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + try_files $uri $uri/ /index.html; + } +} \ No newline at end of file From 3f5f514ae2f066f132b3e8fd0400d28b9504b1b1 Mon Sep 17 00:00:00 2001 From: Jacques Verre Date: Sun, 15 Sep 2024 18:39:06 +0100 Subject: [PATCH 2/7] Updated Opik documentation with new deployment options --- README.md | 4 +- .../evaluate_hallucination_metric.ipynb | 2 +- .../cookbook/evaluate_hallucination_metric.md | 2 +- .../cookbook/evaluate_moderation_metric.ipynb | 2 +- .../cookbook/evaluate_moderation_metric.md | 2 +- .../docs/cookbook/langchain.ipynb | 2 +- .../documentation/docs/cookbook/langchain.md | 2 +- .../docs/cookbook/llama-index.ipynb | 2 +- .../docs/cookbook/llama-index.md | 2 +- .../documentation/docs/cookbook/openai.ipynb | 2 +- .../documentation/docs/cookbook/openai.md | 2 +- .../documentation/docs/cookbook/ragas.ipynb | 2 +- .../documentation/docs/cookbook/ragas.md | 2 +- .../documentation/docs/home.md | 2 +- .../documentation/docs/quickstart.md | 2 +- .../docs/self-host/kubernetes.md | 52 ++ .../docs/self-host/local_deployment.md | 105 +++ .../documentation/docs/self-host/overview.md | 42 ++ .../docs/self-host/self_hosting_opik.md | 128 ---- .../documentation/docusaurus.config.ts | 12 + .../documentation/package-lock.json | 601 ++++++++++++------ .../documentation/package.json | 11 +- .../documentation/sidebars.ts | 2 +- .../python-sdk-docs/source/index.rst | 3 +- sdks/python/src/opik/cli.py | 5 - 25 files changed, 627 insertions(+), 366 deletions(-) create mode 100644 apps/opik-documentation/documentation/docs/self-host/kubernetes.md create mode 100644 apps/opik-documentation/documentation/docs/self-host/local_deployment.md create mode 100644 apps/opik-documentation/documentation/docs/self-host/overview.md delete mode 100644 apps/opik-documentation/documentation/docs/self-host/self_hosting_opik.md diff --git a/README.md b/README.md index e022bde1c..c1be1b038 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,8 @@ For more information about the different deployment options, please see our depl | Installation methods | Docs link | | ------------------- | --------- | -| Local instance | [![All-in-one isntallation](https://img.shields.io/badge/All--in--one%20Installer-%230db7ed)](https://www.comet.com/docs/opik/self-host/self_hosting_opik/#all-in-one-installation?utm_source=opik&utm_medium=github&utm_content=self_host_link) -| Kubernetes | [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?&logo=kubernetes&logoColor=white)](https://www.comet.com/docs/opik/self-host/self_hosting_opik/#kubernetes-installation?utm_source=opik&utm_medium=github&utm_content=kubernetes_link) +| Local instance | [![Local Deployment](https://img.shields.io/badge/Local%20Deployments-%232496ED?style=flat&logo=docker&logoColor=white)](https://www.comet.com/docs/opik/self-host/local_deployment?utm_source=opik&utm_medium=github&utm_content=self_host_link) +| Kubernetes | [![Kubernetes](https://img.shields.io/badge/Kubernetes-%23326ce5.svg?&logo=kubernetes&logoColor=white)](https://www.comet.com/docs/opik/self-host/kubernetes/#kubernetes-installation?utm_source=opik&utm_medium=github&utm_content=kubernetes_link) ## 🏁 Get Started diff --git a/apps/opik-documentation/documentation/docs/cookbook/evaluate_hallucination_metric.ipynb b/apps/opik-documentation/documentation/docs/cookbook/evaluate_hallucination_metric.ipynb index 9982ed2ad..865f42055 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/evaluate_hallucination_metric.ipynb +++ b/apps/opik-documentation/documentation/docs/cookbook/evaluate_hallucination_metric.ipynb @@ -17,7 +17,7 @@ "\n", "[Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key.\n", "\n", - "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information." + "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information." ] }, { diff --git a/apps/opik-documentation/documentation/docs/cookbook/evaluate_hallucination_metric.md b/apps/opik-documentation/documentation/docs/cookbook/evaluate_hallucination_metric.md index 6bd498330..90fde6230 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/evaluate_hallucination_metric.md +++ b/apps/opik-documentation/documentation/docs/cookbook/evaluate_hallucination_metric.md @@ -6,7 +6,7 @@ For this guide we will be evaluating the Hallucination metric included in the LL [Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key. -> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information. +> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information. ```python diff --git a/apps/opik-documentation/documentation/docs/cookbook/evaluate_moderation_metric.ipynb b/apps/opik-documentation/documentation/docs/cookbook/evaluate_moderation_metric.ipynb index 297695853..721217fe8 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/evaluate_moderation_metric.ipynb +++ b/apps/opik-documentation/documentation/docs/cookbook/evaluate_moderation_metric.ipynb @@ -17,7 +17,7 @@ "\n", "[Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key.\n", "\n", - "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information." + "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information." ] }, { diff --git a/apps/opik-documentation/documentation/docs/cookbook/evaluate_moderation_metric.md b/apps/opik-documentation/documentation/docs/cookbook/evaluate_moderation_metric.md index 35efae80a..121a1fdf5 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/evaluate_moderation_metric.md +++ b/apps/opik-documentation/documentation/docs/cookbook/evaluate_moderation_metric.md @@ -6,7 +6,7 @@ For this guide we will be evaluating the Moderation metric included in the LLM E [Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key. -> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information. +> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information. ```python diff --git a/apps/opik-documentation/documentation/docs/cookbook/langchain.ipynb b/apps/opik-documentation/documentation/docs/cookbook/langchain.ipynb index ce37e7e37..ade23154d 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/langchain.ipynb +++ b/apps/opik-documentation/documentation/docs/cookbook/langchain.ipynb @@ -23,7 +23,7 @@ "\n", "[Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key.\n", "\n", - "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information." + "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information." ] }, { diff --git a/apps/opik-documentation/documentation/docs/cookbook/langchain.md b/apps/opik-documentation/documentation/docs/cookbook/langchain.md index 7162456ac..88ebe4be0 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/langchain.md +++ b/apps/opik-documentation/documentation/docs/cookbook/langchain.md @@ -12,7 +12,7 @@ We will highlight three different parts of the workflow: [Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key. -> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information. +> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information. ```python diff --git a/apps/opik-documentation/documentation/docs/cookbook/llama-index.ipynb b/apps/opik-documentation/documentation/docs/cookbook/llama-index.ipynb index 0900a8017..5beba6faa 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/llama-index.ipynb +++ b/apps/opik-documentation/documentation/docs/cookbook/llama-index.ipynb @@ -25,7 +25,7 @@ "\n", "[Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key.\n", "\n", - "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information." + "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information." ] }, { diff --git a/apps/opik-documentation/documentation/docs/cookbook/llama-index.md b/apps/opik-documentation/documentation/docs/cookbook/llama-index.md index bf1218fd4..df20f7cb7 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/llama-index.md +++ b/apps/opik-documentation/documentation/docs/cookbook/llama-index.md @@ -14,7 +14,7 @@ For this guide we will be downloading the essays from Paul Graham and use them a [Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key. -> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information. +> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information. ```python diff --git a/apps/opik-documentation/documentation/docs/cookbook/openai.ipynb b/apps/opik-documentation/documentation/docs/cookbook/openai.ipynb index fb2d27c4c..b86ba42c4 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/openai.ipynb +++ b/apps/opik-documentation/documentation/docs/cookbook/openai.ipynb @@ -17,7 +17,7 @@ "\n", "[Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key.\n", "\n", - "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information." + "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information." ] }, { diff --git a/apps/opik-documentation/documentation/docs/cookbook/openai.md b/apps/opik-documentation/documentation/docs/cookbook/openai.md index eaabd50ff..be704a216 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/openai.md +++ b/apps/opik-documentation/documentation/docs/cookbook/openai.md @@ -7,7 +7,7 @@ Opik integrates with OpenAI to provide a simple way to log traces for all OpenAI [Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key. -> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information. +> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information. ```python diff --git a/apps/opik-documentation/documentation/docs/cookbook/ragas.ipynb b/apps/opik-documentation/documentation/docs/cookbook/ragas.ipynb index a2e20e60c..8a135e32d 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/ragas.ipynb +++ b/apps/opik-documentation/documentation/docs/cookbook/ragas.ipynb @@ -22,7 +22,7 @@ "\n", "[Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key.\n", "\n", - "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information." + "> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information." ] }, { diff --git a/apps/opik-documentation/documentation/docs/cookbook/ragas.md b/apps/opik-documentation/documentation/docs/cookbook/ragas.md index 3288c74a2..748fd850a 100644 --- a/apps/opik-documentation/documentation/docs/cookbook/ragas.md +++ b/apps/opik-documentation/documentation/docs/cookbook/ragas.md @@ -11,7 +11,7 @@ There are two main ways to use Opik with Ragas: [Comet](https://www.comet.com/site) provides a hosted version of the Opik platform, [simply create an account](https://www.comet.com/signup?from=llm) and grab you API Key. -> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/self_hosting_opik/) for more information. +> You can also run the Opik platform locally, see the [installation guide](https://www.comet.com/docs/opik/self-host/overview/) for more information. ```python diff --git a/apps/opik-documentation/documentation/docs/home.md b/apps/opik-documentation/documentation/docs/home.md index 5e51227ff..9f1e91c15 100644 --- a/apps/opik-documentation/documentation/docs/home.md +++ b/apps/opik-documentation/documentation/docs/home.md @@ -37,4 +37,4 @@ Evaluating the output of your LLM calls is critical to ensure that your applicat [Comet](https://www.comet.com/site) provides a managed Cloud offering for Opik, simply [create an account](https://www.comet.com/signup?from=llm) to get started. -You can also run Opik locally using our [local installer](/self-host/self_hosting_opik.md#all-in-one-installation). If you are looking for a more production ready deployment, you can also use our [Kubernetes deployment option](/self-host/self_hosting_opik.md#kubernetes-installation). +You can also run Opik locally using our [local installer](/self-host/local_deployment.md). If you are looking for a more production ready deployment, you can also use our [Kubernetes deployment option](/self-host/kubernetes.md). diff --git a/apps/opik-documentation/documentation/docs/quickstart.md b/apps/opik-documentation/documentation/docs/quickstart.md index 270239aaf..6cc3cb5f8 100644 --- a/apps/opik-documentation/documentation/docs/quickstart.md +++ b/apps/opik-documentation/documentation/docs/quickstart.md @@ -9,7 +9,7 @@ This guide helps you integrate the Opik platform with your existing LLM applicat ## Set up -Getting started is as simple as creating an [account on Comet](https://www.comet.com/signup?from=llm) or [self-hosting the platform](/self-host/self_hosting_opik.md). +Getting started is as simple as creating an [account on Comet](https://www.comet.com/signup?from=llm) or [self-hosting the platform](/self-host/overview.md). Once your account is created, you can start logging traces by installing the Opik Python SDK: diff --git a/apps/opik-documentation/documentation/docs/self-host/kubernetes.md b/apps/opik-documentation/documentation/docs/self-host/kubernetes.md new file mode 100644 index 000000000..de13807b7 --- /dev/null +++ b/apps/opik-documentation/documentation/docs/self-host/kubernetes.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +sidebar_label: Production (Kubernetes) +--- + +# Production ready Kubernetes deployment + +For production deployments, we recommend using our Kubernetes Helm chart. This chart is designed to be highly configurable and has been battle-tested in Comet's managed cloud offering. + + +## Prerequisites +In order to install Opik on a Kubernetes cluster, you will need to have the following tools installed: + +- [Docker](https://www.docker.com/) +- [Helm](https://helm.sh/) +- [kubectl](https://kubernetes.io/docs/tasks/tools/) +- [kubectx](https://github.com/ahmetb/kubectx) and [kubens](https://github.com/ahmetb/kubectx) to switch between Kubernetes clusters and namespaces. + +## Installation + +You will then be able to to install Opik using the helm chart defined in the `deployment/helm_chart/opik` directory of the [Opik repository](https://github.com/comet-ml/opik): + +```bash +# Navigate to the directory +cd deployment/helm_chart/opik + +# Define the version of the Opik server you want to install +VERSION=latest + +# Add helm dependencies +helm repo add bitnami https://charts.bitnami.com/bitnami +helm dependency build + +# Install Opik +helm upgrade --install opik -n llm --create-namespace -f values.yaml \ + --set registry=docker.dev.comet.com/comet-ml \ + --set component.backend.image.tag=$VERSION --set component.frontend.image.tag=$VERSION-os \ + --set component.backend.env.ANALYTICS_DB_MIGRATIONS_PASS=opik --set component.backend.env.ANALYTICS_DB_PASS=opik \ + --set component.backend.env.STATE_DB_PASS=opik . +``` + +To access the Opik UI, you will need to port-forward the frontend service: + +```bash +kubectl port-forward -n llm svc/opik-frontend 5173 +``` + +You can now open the Opik UI at `http://localhost:5173/llm`. + +## Configuration + +You can find a full list the configuration options in the [helm chart documentation](https://github.com/comet-ml/opik/tree/main/deployment/helm_chart/opik). diff --git a/apps/opik-documentation/documentation/docs/self-host/local_deployment.md b/apps/opik-documentation/documentation/docs/self-host/local_deployment.md new file mode 100644 index 000000000..71ff131fd --- /dev/null +++ b/apps/opik-documentation/documentation/docs/self-host/local_deployment.md @@ -0,0 +1,105 @@ +--- +sidebar_position: 1 +sidebar_label: Local (Docker Compose) +--- + +# Local Deployments using Docker Compose + +To run Opik locally we recommend using [Docker Compose](https://docs.docker.com/compose/). It's easy to setup and allows you to get started in a couple of minutes **but** is not meant for production deployments. If you would like to run Opik in a production environment, we recommend using our [Kubernetes Helm chart](./kubernetes.md). + +Before running the installation, make sure you have Docker and Docker Compose installed: + +- [Docker](https://docs.docker.com/get-docker/) +- [Docker Compose](https://docs.docker.com/compose/install/) + +:::note +If you are using Mac or Windows, both `docker` and `docker compose` are included in the [Docker Desktop](https://docs.docker.com/desktop/) installation. +::: + +## Installation + +To install Opik, you will need to clone the Opik repository and run the `docker-compose.yaml` file: + +```bash +# Clone the Opik repository +git clone https://github.com/comet-ml/opik.git + +# Navigate to the opik directory +cd opik + +# Start the Opik platform +docker compose up --detach +``` + +Opik will now be available at `http://localhost:5173`. + +:::tip +You will need to make sure that the Opik Python SDK is configured to point to the Opik server you just started. You can use the `login` CLI command provided by the Opik Python SDK: + +```bash +# Install the Opik Python SDK +pip install opik + +# Follow the prompts to create an SDK configuration file that points to +# the Opik server you just started +opik login --local +``` +::: + +All the data logged to the Opik platform will be stored in the `~/opik` directory, which means that you can start and stop the Opik platform without losing any data. + +## Starting, stopping and upgrading Opik + +The `docker compose up` command can be used to install, start and upgrade Opik: + +```bash +# Start, upgrade or restart the Opik platform +docker compose up --detach +``` + +To stop Opik, you can run: + +```bash +# Stop the Opik platform +docker compose down +``` + +## Removing Opik + +To remove Opik, you will need to remove the Opik containers and volumes: + +```bash +# Remove the Opik containers and volumes +docker compose down --volumes +``` + +:::warning +Removing the volumes will delete all the data stored in the Opik platform and cannot be recovered. We do not recommend this option unless you are sure that you will not need any of the data stored in the Opik platform. +::: + +## Advanced configuration + +### Running a specific version of Opik + +You can run a specific version of Opik by setting the `OPIK_VERSION` environment variable: + +```bash +OPIK_VERSION=latest docker compose up +``` + +### Building the Opik platform from source + +You can also build the Opik platform from source by running the following command: + +```bash +# Clone the Opik repository +git clone https://github.com/comet-ml/opik.git + +# Navigate to the opik directory +cd opik + +# Build the Opik platform from source +docker compose up --build +``` + +This will build the Frontend and Backend Docker images and start the Opik platform. diff --git a/apps/opik-documentation/documentation/docs/self-host/overview.md b/apps/opik-documentation/documentation/docs/self-host/overview.md new file mode 100644 index 000000000..21e081e47 --- /dev/null +++ b/apps/opik-documentation/documentation/docs/self-host/overview.md @@ -0,0 +1,42 @@ +--- +sidebar_position: 1 +sidebar_label: Overview +--- + +# Self-hosting Opik + +You can use Opik through [Comet's Managed Cloud offering](https://comet.com/site) or you can self-host Opik on your own infrastructure. When choosing to self-host Opik, you get access to all Opik features including tracing, evaluation, etc but without user management features. + +If you choose to self-host Opik, you can choose between two deployment options: + +1. [Local installation](./local_deployment.md): Perfect to get started but not production-ready. +2. [Kubernetes installation](./kubernetes.md): Production ready Opik platform that runs on a Kubernetes cluster. + +## Getting started + +If you would like to try out Opik locally, we recommend using our Local installation based on `docker compose`. Assuming you have `git` and `docker` installed, you can get started in a couple of minutes: + +```bash +# Clone the Opik repository +git clone https://github.com/comet-ml/opik.git + +# Run the Opik platform +cd opik +docker compose up --detach + +# Configure the Python SDK to point to the local Opik platform +pip install opik +opik login --local +``` + +Opik will now be available at `http://localhost:5173` and all traces logged from your local machine will be logged to this local Opik instance. + +To learn more about how to manage you local Opik deployment, you can refer to our [local deployment guide](./local_deployment.md). + +## Advanced deployment options + +If you would like to deploy Opik on a Kubernetes cluster, we recommend following our Kubernetes deployment guide [here](./kubernetes.md). + +## Comet managed deployments + +The Opik platform is being developed and maintained by the Comet team. If you are looking for a managed deployment solution, feel free to reach out to the Comet team at sales@comet.com or visit the [Comet website](https://comet.com/site) to learn more. diff --git a/apps/opik-documentation/documentation/docs/self-host/self_hosting_opik.md b/apps/opik-documentation/documentation/docs/self-host/self_hosting_opik.md deleted file mode 100644 index f3c5eb042..000000000 --- a/apps/opik-documentation/documentation/docs/self-host/self_hosting_opik.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -sidebar_position: 1 -sidebar_label: Overview ---- - -# Self-host - -You can use Opik through [Comet's Managed Cloud offering](https://comet.com/site) or you can self-host Opik on your own infrastructure. When choosing to self-host Opik, you get access to all Opik features including tracing, evaluation, etc but without user management features. - -If you choose to self-host Opik, you can choose between two deployment options: - -1. All-in-one installation: The Opik platform runs on a single server. -2. Kubernetes installation: The Opik platform runs on a Kubernetes cluster. - -If you are looking at just getting started, we recommend the all-in-one installation. For more advanced use cases, you can choose the Kubernetes installation. - -## All-in-one installation - -The all-in-one installer is the easiest way to get started with Opik. - -### Installation - -To install the Opik server, run the following command: - -```bash -opik-server install -``` - -You can also run the installer in debug mode to see the details of the -installation process: - -```bash -opik-server --debug install -``` - -:::tip -We recommend installing using the `--debug` flag as the installation can take a couple of minutes -::: - -The opik installer has been tested on the following operating systems: - -- Ubuntu 22.04 -- MacOS - -By default, the installer will install the same version of the Opik as its -own version (`opik-server -v`). If you want to install a specific version, you -can specify the version using the `--opik-version` flag: - -```bash -opik-server install --opik-version 0.1.0 -``` - -By default, the installer will setup a local port forward to the Opik server -using the port `5173`. If you want to use a different port, you can specify -the port using the `--local-port` flag: - -```bash -opik-server install --local-port 5174 -``` - -The installation process takes a couple of minutes and when complete, Opik will be available at `http://localhost:5173`. - -### Upgrading the Opik server - -To upgrade the Opik server, run the following command: - -```bash -pip install --upgrade opik-server -opik-server upgrade -``` - -Or upgrade to a specific version: - -```bash -opik-server upgrade --opik-version 0.1.1 -``` - -### Uninstalling the Opik server - -To uninstall the Opik server, you can run the following command: - -```bash -minikube delete -``` - -## Kubernetes installation - -If you are looking for a more customization options, you can choose to install Opik on a Kubernetes cluster. - -In order to install Opik on a Kubernetes cluster, you will need to have the following tools installed: - -- [Docker](https://www.docker.com/) -- [Helm](https://helm.sh/) -- [kubectl](https://kubernetes.io/docs/tasks/tools/) -- [kubectx](https://github.com/ahmetb/kubectx) and [kubens](https://github.com/ahmetb/kubectx) to switch between Kubernetes clusters and namespaces. - -To install Opik, you can use the helm chart defined in the `deployment/helm_chart/opik` directory of the [Opik repository](https://github.com/comet-ml/opik): - -```bash -# Navigate to the directory -cd deployment/helm_chart/opik - -# Define the version of the Opik server you want to install -VERSION=main - -# Add helm dependencies -helm repo add bitnami https://charts.bitnami.com/bitnami -helm dependency build - -# Install Opik -helm upgrade --install opik -n llm --create-namespace -f values.yaml \ - --set registry=docker.dev.comet.com/comet-ml \ - --set component.backend.image.tag=$VERSION --set component.frontend.image.tag=$VERSION-os \ - --set component.backend.env.ANALYTICS_DB_MIGRATIONS_PASS=opik --set component.backend.env.ANALYTICS_DB_PASS=opik \ - --set component.backend.env.STATE_DB_PASS=opik . -``` - -To access the Opik UI, you will need to port-forward the frontend service: - -```bash -kubectl port-forward -n llm svc/opik-frontend 5173 -``` - -You can now open the Opik UI at `http://localhost:5173/llm`. - -### Configuration - -You can find a full list the configuration options in the [helm chart documentation](https://github.com/comet-ml/opik/tree/main/deployment/helm_chart/opik). diff --git a/apps/opik-documentation/documentation/docusaurus.config.ts b/apps/opik-documentation/documentation/docusaurus.config.ts index 5fa11bc1b..71d2aebee 100644 --- a/apps/opik-documentation/documentation/docusaurus.config.ts +++ b/apps/opik-documentation/documentation/docusaurus.config.ts @@ -64,6 +64,17 @@ const config: Config = { searchResultLimits: 25, docsRouteBasePath: "/docs/opik" }, + ], + [ + '@docusaurus/plugin-client-redirects', + { + redirects: [ + { + to: '/self-host/overview', + from: ['/self-host/self_hosting_opik'], + }, + ] + }, ] ], @@ -93,6 +104,7 @@ const config: Config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, + additionalLanguages: ['bash'], }, } satisfies Preset.ThemeConfig, diff --git a/apps/opik-documentation/documentation/package-lock.json b/apps/opik-documentation/documentation/package-lock.json index 8994e1d10..832a1fa38 100644 --- a/apps/opik-documentation/documentation/package-lock.json +++ b/apps/opik-documentation/documentation/package-lock.json @@ -8,8 +8,9 @@ "name": "documentation", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/preset-classic": "3.4.0", + "@docusaurus/core": "^3.5.2", + "@docusaurus/plugin-client-redirects": "^3.5.2", + "@docusaurus/preset-classic": "^3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "docusaurus-plugin-sass": "^0.2.5", @@ -20,9 +21,9 @@ "sass": "^1.77.8" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/tsconfig": "3.4.0", - "@docusaurus/types": "3.4.0", + "@docusaurus/module-type-aliases": "^3.5.2", + "@docusaurus/tsconfig": "^3.5.2", + "@docusaurus/types": "^3.5.2", "concurrently": "^8.2.0", "nodemon": "^2.0.22", "typescript": "~5.2.2" @@ -111,6 +112,27 @@ "@algolia/transporter": "4.24.0" } }, + "node_modules/@algolia/client-account/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-account/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, "node_modules/@algolia/client-analytics": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.24.0.tgz", @@ -123,7 +145,7 @@ "@algolia/transporter": "4.24.0" } }, - "node_modules/@algolia/client-common": { + "node_modules/@algolia/client-analytics/node_modules/@algolia/client-common": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", @@ -133,6 +155,27 @@ "@algolia/transporter": "4.24.0" } }, + "node_modules/@algolia/client-analytics/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.4.1.tgz", + "integrity": "sha512-IffPD+CETiR8YJMVC1lcjnhETLpJ2L0ORZCbbRvwo/S11D1j/keR7AqKVMn4TseRJCfjmBFOcFrC+m4sXjyQWA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/client-personalization": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.24.0.tgz", @@ -144,17 +187,32 @@ "@algolia/transporter": "4.24.0" } }, - "node_modules/@algolia/client-search": { + "node_modules/@algolia/client-personalization/node_modules/@algolia/client-common": { "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", - "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "4.24.0", "@algolia/requester-common": "4.24.0", "@algolia/transporter": "4.24.0" } }, + "node_modules/@algolia/client-search": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.4.1.tgz", + "integrity": "sha512-nCgWY2p0tZgBqJKmA5E6B3VW+7uqxi1Orf88zNWOihJBRFeOV932pzG4vGrX9l0+p0o/vJabYxuomO35rEt5dw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.4.1", + "@algolia/requester-browser-xhr": "5.4.1", + "@algolia/requester-fetch": "5.4.1", + "@algolia/requester-node-http": "5.4.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/events": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", @@ -195,7 +253,28 @@ "@algolia/transporter": "4.24.0" } }, - "node_modules/@algolia/requester-browser-xhr": { + "node_modules/@algolia/recommend/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/recommend/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/recommend/node_modules/@algolia/requester-browser-xhr": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", @@ -204,19 +283,58 @@ "@algolia/requester-common": "4.24.0" } }, + "node_modules/@algolia/recommend/node_modules/@algolia/requester-node-http": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", + "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.4.1.tgz", + "integrity": "sha512-J6+YfU+maR0nIbsYRHoq0UpneilX97hrZzPuuvSoBojQmPo8PeCXKGeT/F0D8uFI6G4CMTKEPGmQYrC9IpCbcQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.4.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/requester-common": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz", "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==", "license": "MIT" }, + "node_modules/@algolia/requester-fetch": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.4.1.tgz", + "integrity": "sha512-AO/C1pqqpIS8p2IsfM5x92S+UBKkcIen5dHfMEh1rnV0ArWDreeqrtxMD2A+6AjQVwYeZNy56w7o7PVIm6mc8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.4.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@algolia/requester-node-http": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", - "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.4.1.tgz", + "integrity": "sha512-2Y3vffc91egwFxz0SjXFEH4q8nvlNJHcz+0//NaWItRU68AvD+3aI/j66STPjkLQOC0Ku6ckA9ChhbOVfrv+Uw==", "license": "MIT", + "peer": true, "dependencies": { - "@algolia/requester-common": "4.24.0" + "@algolia/client-common": "5.4.1" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/transporter": { @@ -2290,9 +2408,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.4.0.tgz", - "integrity": "sha512-g+0wwmN2UJsBqy2fQRQ6fhXruoEa62JDeEa5d8IdTJlMoaDaEDfHh7WjwGRn4opuTQWpjAwP/fbcgyHKlE+64w==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.5.2.tgz", + "integrity": "sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==", "license": "MIT", "dependencies": { "@babel/core": "^7.23.3", @@ -2305,12 +2423,12 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/cssnano-preset": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -2371,14 +2489,15 @@ "node": ">=18.0" }, "peerDependencies": { + "@mdx-js/react": "^3.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.4.0.tgz", - "integrity": "sha512-qwLFSz6v/pZHy/UP32IrprmH5ORce86BGtN0eBtG75PpzQJAzp9gefspox+s8IEOr0oZKuQ/nhzZ3xwyc3jYJQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.5.2.tgz", + "integrity": "sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==", "license": "MIT", "dependencies": { "cssnano-preset-advanced": "^6.1.2", @@ -2391,9 +2510,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.4.0.tgz", - "integrity": "sha512-bZwkX+9SJ8lB9kVRkXw+xvHYSMGG4bpYHKGXeXFvyVc79NMeeBSGgzd4TQLHH+DYeOJoCdl8flrFJVxlZ0wo/Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.5.2.tgz", + "integrity": "sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==", "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -2404,14 +2523,14 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.4.0.tgz", - "integrity": "sha512-kSSbrrk4nTjf4d+wtBA9H+FGauf2gCax89kV8SUSJu3qaTdSIKdWERlngsiHaCFgZ7laTJ8a67UFf+xlFPtuTw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.5.2.tgz", + "integrity": "sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2443,12 +2562,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.4.0.tgz", - "integrity": "sha512-A1AyS8WF5Bkjnb8s+guTDuYmUiwJzNrtchebBHpc0gz0PyHJNMaybUlSrmJjHVcGrya0LKI4YcR3lBDQfXRYLw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz", + "integrity": "sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.4.0", + "@docusaurus/types": "3.5.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2461,20 +2580,45 @@ "react-dom": "*" } }, + "node_modules/@docusaurus/plugin-client-redirects": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.5.2.tgz", + "integrity": "sha512-GMU0ZNoVG1DEsZlBbwLPdh0iwibrVZiRfmdppvX17SnByCVP74mb/Nne7Ss7ALgxQLtM4IHbXi8ij90VVjAJ+Q==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "eta": "^2.2.0", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.4.0.tgz", - "integrity": "sha512-vv6ZAj78ibR5Jh7XBUT4ndIjmlAxkijM3Sx5MAAzC1gyv0vupDQNhzuFg1USQmQVj3P5I6bquk12etPV3LJ+Xw==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", - "cheerio": "^1.0.0-rc.12", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz", + "integrity": "sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", "lodash": "^4.17.21", @@ -2489,24 +2633,26 @@ "node": ">=18.0" }, "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.4.0.tgz", - "integrity": "sha512-HkUCZffhBo7ocYheD9oZvMcDloRnGhBMOZRyVcAQRFmZPmNqSyISlXA1tQCIxW+r478fty97XXAGjNYzBjpCsg==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz", + "integrity": "sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2525,16 +2671,16 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.4.0.tgz", - "integrity": "sha512-h2+VN/0JjpR8fIkDEAoadNjfR3oLzB+v1qSXbIAKjQ46JAHx3X22n9nqS+BWSQnTnp1AjkjSvZyJMekmcwxzxg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz", + "integrity": "sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -2548,14 +2694,14 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.4.0.tgz", - "integrity": "sha512-uV7FDUNXGyDSD3PwUaf5YijX91T5/H9SX4ErEcshzwgzWwBtK37nUWPU3ZLJfeTavX3fycTOqk9TglpOLaWkCg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.5.2.tgz", + "integrity": "sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2569,14 +2715,14 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.4.0.tgz", - "integrity": "sha512-mCArluxEGi3cmYHqsgpGGt3IyLCrFBxPsxNZ56Mpur0xSlInnIHoeLDH7FvVVcPJRPSQ9/MfRqLsainRw+BojA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.5.2.tgz", + "integrity": "sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "tslib": "^2.6.0" }, "engines": { @@ -2588,14 +2734,14 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.4.0.tgz", - "integrity": "sha512-Dsgg6PLAqzZw5wZ4QjUYc8Z2KqJqXxHxq3vIoyoBWiLEEfigIs7wHR+oiWUQy3Zk9MIk6JTYj7tMoQU0Jm3nqA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.5.2.tgz", + "integrity": "sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2608,14 +2754,14 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.4.0.tgz", - "integrity": "sha512-O9tX1BTwxIhgXpOLpFDueYA9DWk69WCbDRrjYoMQtFHSkTyE7RhNgyjSPREUWJb9i+YUg3OrsvrBYRl64FCPCQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.5.2.tgz", + "integrity": "sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "tslib": "^2.6.0" }, "engines": { @@ -2627,17 +2773,17 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.4.0.tgz", - "integrity": "sha512-+0VDvx9SmNrFNgwPoeoCha+tRoAjopwT0+pYO1xAbyLcewXSemq+eLxEa46Q1/aoOaJQ0qqHELuQM7iS2gp33Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.5.2.tgz", + "integrity": "sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2651,24 +2797,24 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.4.0.tgz", - "integrity": "sha512-Ohj6KB7siKqZaQhNJVMBBUzT3Nnp6eTKqO+FXO3qu/n1hJl3YLwVKTWBg28LF7MWrKu46UuYavwMRxud0VyqHg==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/plugin-debug": "3.4.0", - "@docusaurus/plugin-google-analytics": "3.4.0", - "@docusaurus/plugin-google-gtag": "3.4.0", - "@docusaurus/plugin-google-tag-manager": "3.4.0", - "@docusaurus/plugin-sitemap": "3.4.0", - "@docusaurus/theme-classic": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-search-algolia": "3.4.0", - "@docusaurus/types": "3.4.0" + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.5.2.tgz", + "integrity": "sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/plugin-content-blog": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/plugin-content-pages": "3.5.2", + "@docusaurus/plugin-debug": "3.5.2", + "@docusaurus/plugin-google-analytics": "3.5.2", + "@docusaurus/plugin-google-gtag": "3.5.2", + "@docusaurus/plugin-google-tag-manager": "3.5.2", + "@docusaurus/plugin-sitemap": "3.5.2", + "@docusaurus/theme-classic": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-search-algolia": "3.5.2", + "@docusaurus/types": "3.5.2" }, "engines": { "node": ">=18.0" @@ -2679,27 +2825,27 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.4.0.tgz", - "integrity": "sha512-0IPtmxsBYv2adr1GnZRdMkEQt1YW6tpzrUPj02YxNpvJ5+ju4E13J5tB4nfdaen/tfR1hmpSPlTFPvTf4kwy8Q==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/types": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.5.2.tgz", + "integrity": "sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/plugin-content-blog": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/plugin-content-pages": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-translations": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.43", + "infima": "0.2.0-alpha.44", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.26", @@ -2719,18 +2865,15 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.4.0.tgz", - "integrity": "sha512-0A27alXuv7ZdCg28oPE8nH/Iz73/IUejVaCazqu9elS4ypjiLhK3KfzdSQBnL/g7YfHSlymZKdiOHEo8fJ0qMA==", - "license": "MIT", - "dependencies": { - "@docusaurus/mdx-loader": "3.4.0", - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/plugin-content-blog": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/plugin-content-pages": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.5.2.tgz", + "integrity": "sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew==", + "license": "MIT", + "dependencies": { + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2744,24 +2887,25 @@ "node": ">=18.0" }, "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.4.0.tgz", - "integrity": "sha512-aiHFx7OCw4Wck1z6IoShVdUWIjntC8FHCw9c5dR8r3q4Ynh+zkS8y2eFFunN/DL6RXPzpnvKCg3vhLQYJDmT9Q==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz", + "integrity": "sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA==", "license": "MIT", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.4.0", - "@docusaurus/logger": "3.4.0", - "@docusaurus/plugin-content-docs": "3.4.0", - "@docusaurus/theme-common": "3.4.0", - "@docusaurus/theme-translations": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-validation": "3.4.0", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-translations": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2780,9 +2924,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.4.0.tgz", - "integrity": "sha512-zSxCSpmQCCdQU5Q4CnX/ID8CSUUI3fvmq4hU/GNP/XoAWtXo9SAVnM3TzpU8Gb//H3WCsT8mJcTfyOk3d9ftNg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz", + "integrity": "sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==", "license": "MIT", "dependencies": { "fs-extra": "^11.1.1", @@ -2793,16 +2937,16 @@ } }, "node_modules/@docusaurus/tsconfig": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.4.0.tgz", - "integrity": "sha512-0qENiJ+TRaeTzcg4olrnh0BQ7eCxTgbYWBnWUeQDc84UYkt/T3pDNnm3SiQkqPb+YQ1qtYFlC0RriAElclo8Dg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.5.2.tgz", + "integrity": "sha512-rQ7toURCFnWAIn8ubcquDs0ewhPwviMzxh6WpRjBW7sJVCXb6yzwUaY3HMNa0VXCFw+qkIbFywrMTf+Pb4uHWQ==", "dev": true, "license": "MIT" }, "node_modules/@docusaurus/types": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.4.0.tgz", - "integrity": "sha512-4jcDO8kXi5Cf9TcyikB/yKmz14f2RZ2qTRerbHAsS+5InE9ZgSLBNLsewtFTcTOXSVcbU3FoGOzcNWAmU1TR0A==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.5.2.tgz", + "integrity": "sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==", "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.0.0", @@ -2821,13 +2965,13 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.4.0.tgz", - "integrity": "sha512-fRwnu3L3nnWaXOgs88BVBmG1yGjcQqZNHG+vInhEa2Sz2oQB+ZjbEMO5Rh9ePFpZ0YDiDUhpaVjwmS+AU2F14g==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.5.2.tgz", + "integrity": "sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "@svgr/webpack": "^8.1.0", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -2860,9 +3004,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.4.0.tgz", - "integrity": "sha512-NVx54Wr4rCEKsjOH5QEVvxIqVvm+9kh7q8aYTU5WzUU9/Hctd6aTrcZ3G0Id4zYJ+AeaG5K5qHA4CY5Kcm2iyQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.5.2.tgz", + "integrity": "sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==", "license": "MIT", "dependencies": { "tslib": "^2.6.0" @@ -2880,14 +3024,14 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.4.0.tgz", - "integrity": "sha512-hYQ9fM+AXYVTWxJOT1EuNaRnrR2WGpRdLDQG07O8UOpsvCPWUVOeo26Rbm0JWY2sGLfzAb+tvJ62yF+8F+TV0g==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz", + "integrity": "sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.4.0", - "@docusaurus/utils": "3.4.0", - "@docusaurus/utils-common": "3.4.0", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -4173,9 +4317,9 @@ } }, "node_modules/algoliasearch-helper": { - "version": "3.22.3", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.3.tgz", - "integrity": "sha512-2eoEz8mG4KHE+DzfrBTrCmDPxVXv7aZZWPojAJFtARpxxMO6lkos1dJ+XDCXdPvq7q3tpYWRi6xXmVQikejtpA==", + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.5.tgz", + "integrity": "sha512-lWvhdnc+aKOKx8jyA3bsdEgHzm/sglC4cYdMG4xSQyRiPLJVJtH/IVYZG3Hp6PkTEhQqhyVYkeP9z2IlcHJsWw==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" @@ -4184,6 +4328,45 @@ "algoliasearch": ">= 3.1 < 6" } }, + "node_modules/algoliasearch/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/requester-browser-xhr": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", + "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/requester-node-http": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", + "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -6289,9 +6472,9 @@ } }, "node_modules/emoticon": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.0.1.tgz", - "integrity": "sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/emoticon/-/emoticon-4.1.0.tgz", + "integrity": "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ==", "license": "MIT", "funding": { "type": "github", @@ -8239,9 +8422,9 @@ } }, "node_modules/infima": { - "version": "0.2.0-alpha.43", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", - "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", + "version": "0.2.0-alpha.44", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.44.tgz", + "integrity": "sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ==", "license": "MIT", "engines": { "node": ">=12" @@ -9145,9 +9328,9 @@ } }, "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", - "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -13159,9 +13342,9 @@ "license": "MIT" }, "node_modules/react-json-view-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.4.0.tgz", - "integrity": "sha512-wh6F6uJyYAmQ4fK0e8dSQMEWuvTs2Wr3el3sLD9bambX1+pSWUVXIz1RFaoy3TI1mZ0FqdpKq9YgbgTTgyrmXA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.5.0.tgz", + "integrity": "sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==", "license": "MIT", "engines": { "node": ">=14" @@ -13794,9 +13977,9 @@ "license": "BSD-3-Clause" }, "node_modules/rtlcss": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.2.0.tgz", - "integrity": "sha512-AV+V3oOVvCrqyH5Q/6RuT1IDH1Xy5kJTkEWTWZPN5rdQ3HCFOd8SrbC7c6N5Y8bPpCfZSR6yYbUATXslvfvu5g==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", + "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", "license": "MIT", "dependencies": { "escalade": "^3.1.1", @@ -14008,9 +14191,9 @@ } }, "node_modules/search-insights": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.15.0.tgz", - "integrity": "sha512-ch2sPCUDD4sbPQdknVl9ALSi9H7VyoeVbsxznYz6QV55jJ8CI3EtwpO1i84keN4+hF5IeHWIeGvc08530JkVXQ==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.2.tgz", + "integrity": "sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==", "license": "MIT", "peer": true }, diff --git a/apps/opik-documentation/documentation/package.json b/apps/opik-documentation/documentation/package.json index 736b9c4ec..53d49ef88 100644 --- a/apps/opik-documentation/documentation/package.json +++ b/apps/opik-documentation/documentation/package.json @@ -16,8 +16,9 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "3.4.0", - "@docusaurus/preset-classic": "3.4.0", + "@docusaurus/core": "^3.5.2", + "@docusaurus/plugin-client-redirects": "^3.5.2", + "@docusaurus/preset-classic": "^3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "docusaurus-plugin-sass": "^0.2.5", @@ -28,9 +29,9 @@ "sass": "^1.77.8" }, "devDependencies": { - "@docusaurus/module-type-aliases": "3.4.0", - "@docusaurus/tsconfig": "3.4.0", - "@docusaurus/types": "3.4.0", + "@docusaurus/module-type-aliases": "^3.5.2", + "@docusaurus/tsconfig": "^3.5.2", + "@docusaurus/types": "^3.5.2", "concurrently": "^8.2.0", "nodemon": "^2.0.22", "typescript": "~5.2.2" diff --git a/apps/opik-documentation/documentation/sidebars.ts b/apps/opik-documentation/documentation/sidebars.ts index 39b7d75d9..c170b2d4f 100644 --- a/apps/opik-documentation/documentation/sidebars.ts +++ b/apps/opik-documentation/documentation/sidebars.ts @@ -18,7 +18,7 @@ const sidebars: SidebarsConfig = { type: 'category', label: 'Self-host', collapsed: false, - items: ['self-host/self_hosting_opik'] + items: ['self-host/overview', 'self-host/local_deployment', 'self-host/kubernetes'] }, { type: 'category', diff --git a/apps/opik-documentation/python-sdk-docs/source/index.rst b/apps/opik-documentation/python-sdk-docs/source/index.rst index 496c73f9e..86e3d923d 100644 --- a/apps/opik-documentation/python-sdk-docs/source/index.rst +++ b/apps/opik-documentation/python-sdk-docs/source/index.rst @@ -23,8 +23,7 @@ To get start with the package, you can install it using pip:: pip install opik -By default, all traces, datasets and experiments will be logged to the Comet Cloud platform. If you -would like to self-host the platform, you can refer to our `self-serve documentation `_. +To finish configuring the Opik Python SDK, you recommend running the `opik login` command. ============= Using the SDK diff --git a/sdks/python/src/opik/cli.py b/sdks/python/src/opik/cli.py index 0a1a9e62e..b51509c4b 100644 --- a/sdks/python/src/opik/cli.py +++ b/sdks/python/src/opik/cli.py @@ -4,8 +4,6 @@ import click -from opik_installer import opik_server - __version__: str = "0.0.0+dev" if __package__: __version__ = metadata.version(__package__) @@ -19,8 +17,5 @@ def cli() -> None: """CLI tool for Opik.""" -cli.add_command(opik_server, name="server") - - if __name__ == "__main__": cli() From 98698f04224df50365014b647204725ce2530d47 Mon Sep 17 00:00:00 2001 From: Thiago dos Santos Hora Date: Mon, 16 Sep 2024 12:27:20 +0200 Subject: [PATCH 3/7] [NA] Add info logs (#242) --- .../resources/v1/priv/DatasetsResource.java | 118 ++++++++++++++---- .../v1/priv/FeedbackDefinitionResource.java | 44 +++++-- .../resources/v1/priv/ProjectsResource.java | 39 +++++- .../api/resources/v1/priv/SpansResource.java | 64 ++++++---- .../api/resources/v1/priv/TraceResource.java | 65 +++++++++- .../com/comet/opik/domain/DatasetDAO.java | 3 - .../comet/opik/domain/DatasetItemService.java | 8 +- .../com/comet/opik/domain/DatasetService.java | 19 ++- .../opik/domain/ExperimentItemService.java | 19 +-- .../comet/opik/domain/ExperimentService.java | 9 +- .../opik/domain/FeedbackDefinitionDAO.java | 3 - .../domain/FeedbackDefinitionService.java | 21 ++-- .../opik/domain/FeedbackScoreService.java | 9 +- .../com/comet/opik/domain/ProjectDAO.java | 3 - .../com/comet/opik/domain/ProjectService.java | 43 ++++--- .../com/comet/opik/domain/SpanService.java | 5 +- .../com/comet/opik/domain/TraceService.java | 4 + 17 files changed, 347 insertions(+), 129 deletions(-) diff --git a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/DatasetsResource.java b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/DatasetsResource.java index e90b80455..cb54d09b5 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/DatasetsResource.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/DatasetsResource.java @@ -98,7 +98,13 @@ public class DatasetsResource { @JsonView(Dataset.View.Public.class) public Response getDatasetById(@PathParam("id") UUID id) { - return Response.ok().entity(service.findById(id)).build(); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Finding dataset by id '{}' on workspaceId '{}'", id, workspaceId); + Dataset dataset = service.findById(id); + log.info("Found dataset by id '{}' on workspaceId '{}'", id, workspaceId); + + return Response.ok().entity(dataset).build(); } @GET @@ -115,7 +121,13 @@ public Response findDatasets( .name(name) .build(); - return Response.ok(service.find(page, size, criteria)).build(); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Finding datasets by '{}' on workspaceId '{}'", criteria, workspaceId); + DatasetPage datasetPage = service.find(page, size, criteria); + log.info("Found datasets by '{}', count '{}' on workspaceId '{}'", criteria, datasetPage.size(), workspaceId); + + return Response.ok(datasetPage).build(); } @POST @@ -128,7 +140,14 @@ public Response createDataset( @RequestBody(content = @Content(schema = @Schema(implementation = Dataset.class))) @JsonView(Dataset.View.Write.class) @NotNull @Valid Dataset dataset, @Context UriInfo uriInfo) { - URI uri = uriInfo.getAbsolutePathBuilder().path("/%s".formatted(service.save(dataset).id().toString())).build(); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Creating dataset with name '{}', on workspace_id '{}'", dataset.name(), workspaceId); + Dataset savedDataset = service.save(dataset); + log.info("Created dataset with name '{}', id '{}', on workspace_id '{}'", savedDataset.name(), + savedDataset.id(), workspaceId); + + URI uri = uriInfo.getAbsolutePathBuilder().path("/%s".formatted(savedDataset.id().toString())).build(); return Response.created(uri).build(); } @@ -140,7 +159,11 @@ public Response createDataset( public Response updateDataset(@PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = DatasetUpdate.class))) @NotNull @Valid DatasetUpdate datasetUpdate) { + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Updating dataset by id '{}' on workspace_id '{}'", id, workspaceId); service.update(id, datasetUpdate); + log.info("Updated dataset by id '{}' on workspace_id '{}'", id, workspaceId); + return Response.noContent().build(); } @@ -151,7 +174,10 @@ public Response updateDataset(@PathParam("id") UUID id, }) public Response deleteDataset(@PathParam("id") UUID id) { + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Deleting dataset by id '{}' on workspace_id '{}'", id, workspaceId); service.delete(id); + log.info("Deleted dataset by id '{}' on workspace_id '{}'", id, workspaceId); return Response.noContent().build(); } @@ -163,7 +189,12 @@ public Response deleteDataset(@PathParam("id") UUID id) { public Response deleteDatasetByName( @RequestBody(content = @Content(schema = @Schema(implementation = DatasetIdentifier.class))) @NotNull @Valid DatasetIdentifier identifier) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Deleting dataset by name '{}' on workspace_id '{}'", identifier.datasetName(), workspaceId); service.delete(identifier); + log.info("Deleted dataset by name '{}' on workspace_id '{}'", identifier.datasetName(), workspaceId); + return Response.noContent().build(); } @@ -179,7 +210,11 @@ public Response getDatasetByIdentifier( String workspaceId = requestContext.get().getWorkspaceId(); String name = identifier.datasetName(); - return Response.ok(service.findByName(workspaceId, name)).build(); + log.info("Finding dataset by name '{}' on workspace_id '{}'", name, workspaceId); + Dataset dataset = service.findByName(workspaceId, name); + log.info("Found dataset by name '{}', id '{}' on workspace_id '{}'", name, dataset.id(), workspaceId); + + return Response.ok(dataset).build(); } // Dataset Item Resources @@ -192,9 +227,15 @@ public Response getDatasetByIdentifier( @JsonView(DatasetItem.View.Public.class) public Response getDatasetItemById(@PathParam("itemId") @NotNull UUID itemId) { - return Response.ok(itemService.get(itemId) + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Finding dataset item by id '{}' on workspace_id '{}'", itemId, workspaceId); + DatasetItem datasetItem = itemService.get(itemId) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) - .block()).build(); + .block(); + log.info("Found dataset item by id '{}' on workspace_id '{}'", itemId, workspaceId); + + return Response.ok(datasetItem).build(); } @GET @@ -208,10 +249,16 @@ public Response getDatasetItems( @QueryParam("page") @Min(1) @DefaultValue("1") int page, @QueryParam("size") @Min(1) @DefaultValue("10") int size) { - return Response.ok(itemService.getItems(id, page, size) + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Finding dataset items by id '{}', page '{}', size '{} on workspace_id '{}''", id, page, size, + workspaceId); + DatasetItem.DatasetItemPage datasetItemPage = itemService.getItems(id, page, size) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) - .block()) - .build(); + .block(); + log.info("Found dataset items by id '{}', count '{}', page '{}', size '{} on workspace_id '{}''", id, + datasetItemPage.content().size(), page, size, workspaceId); + + return Response.ok(datasetItemPage).build(); } @POST @@ -226,12 +273,14 @@ public Response getDatasetItems( public ChunkedOutput streamDatasetItems( @RequestBody(content = @Content(schema = @Schema(implementation = DatasetItemStreamRequest.class))) @NotNull @Valid DatasetItemStreamRequest request) { + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Streaming dataset items by '{}' on workspaceId '{}'", request, workspaceId); return getOutputStream(request, request.steamLimit()); } private ChunkedOutput getOutputStream(DatasetItemStreamRequest request, int limit) { - final ChunkedOutput outputStream = new ChunkedOutput<>(JsonNode.class, "\r\n"); + ChunkedOutput outputStream = new ChunkedOutput<>(JsonNode.class, "\r\n"); String workspaceId = requestContext.get().getWorkspaceId(); String userName = requestContext.get().getUserName(); @@ -239,16 +288,21 @@ private ChunkedOutput getOutputStream(DatasetItemStreamRequest request Schedulers .boundedElastic() - .schedule(() -> Mono.fromCallable(() -> service.findByName(workspaceId, request.datasetName())) - .subscribeOn(Schedulers.boundedElastic()) - .flatMapMany(dataset -> itemService.getItems(dataset.id(), limit, request.lastRetrievedId())) - .doOnNext(item -> sendDatasetItems(item, outputStream)) - .onErrorResume(ex -> errorHandling(ex, outputStream)) - .doFinally(signalType -> closeOutput(outputStream)) - .contextWrite(ctx -> ctx.put(RequestContext.USER_NAME, userName) - .put(RequestContext.WORKSPACE_NAME, workspaceName) - .put(RequestContext.WORKSPACE_ID, workspaceId)) - .subscribe()); + .schedule(() -> { + Mono.fromCallable(() -> service.findByName(workspaceId, request.datasetName())) + .subscribeOn(Schedulers.boundedElastic()) + .flatMapMany( + dataset -> itemService.getItems(dataset.id(), limit, request.lastRetrievedId())) + .doOnNext(item -> sendDatasetItems(item, outputStream)) + .onErrorResume(ex -> errorHandling(ex, outputStream)) + .doFinally(signalType -> closeOutput(outputStream)) + .contextWrite(ctx -> ctx.put(RequestContext.USER_NAME, userName) + .put(RequestContext.WORKSPACE_NAME, workspaceName) + .put(RequestContext.WORKSPACE_ID, workspaceId)) + .subscribe(); + + log.info("Streamed dataset items by '{}' on workspaceId '{}'", request, workspaceId); + }); return outputStream; } @@ -304,10 +358,16 @@ public Response createDatasetItems( return item; }).toList(); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Creating dataset items batch by datasetId '{}', datasetName '{}', size '{}' on workspaceId '{}'", + batch.datasetId(), batch.datasetId(), batch.items().size(), workspaceId); itemService.save(new DatasetItemBatch(batch.datasetName(), batch.datasetId(), items)) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .retryWhen(AsyncUtils.handleConnectionError()) .block(); + log.info("Created dataset items batch by datasetId '{}', datasetName '{}', size '{}' on workspaceId '{}'", + batch.datasetId(), batch.datasetId(), batch.items().size(), workspaceId); return Response.noContent().build(); } @@ -320,9 +380,14 @@ public Response createDatasetItems( public Response deleteDatasetItems( @RequestBody(content = @Content(schema = @Schema(implementation = DatasetItemsDelete.class))) @NotNull @Valid DatasetItemsDelete request) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Deleting dataset items by size'{}' on workspaceId '{}'", request, workspaceId); itemService.delete(request.itemIds()) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); + log.info("Deleted dataset items by size'{}' on workspaceId '{}'", request, workspaceId); + return Response.noContent().build(); } @@ -347,13 +412,18 @@ public Response findDatasetItemsWithExperimentItems( .entityType(FeedbackScoreDAO.EntityType.TRACE) .build(); - log.info("Finding dataset items with experiment items by '{}', page '{}', size '{}'", - datasetItemSearchCriteria, page, size); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Finding dataset items with experiment items by '{}', page '{}', size '{}' on workspaceId '{}'", + datasetItemSearchCriteria, page, size, workspaceId); + var datasetItemPage = itemService.getItems(page, size, datasetItemSearchCriteria) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); - log.info("Found dataset items with experiment items by '{}', count '{}', page '{}', size '{}'", - datasetItemSearchCriteria, datasetItemPage.content().size(), page, size); + + log.info( + "Found dataset items with experiment items by '{}', count '{}', page '{}', size '{}' on workspaceId '{}'", + datasetItemSearchCriteria, datasetItemPage.content().size(), page, size, workspaceId); return Response.ok(datasetItemPage).build(); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/FeedbackDefinitionResource.java b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/FeedbackDefinitionResource.java index 1a71beedc..c3a2d9ccc 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/FeedbackDefinitionResource.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/FeedbackDefinitionResource.java @@ -3,7 +3,9 @@ import com.codahale.metrics.annotation.Timed; import com.comet.opik.api.FeedbackDefinition; import com.comet.opik.api.FeedbackDefinitionCriteria; +import com.comet.opik.api.Page; import com.comet.opik.domain.FeedbackDefinitionService; +import com.comet.opik.infrastructure.auth.RequestContext; import com.fasterxml.jackson.annotation.JsonView; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.headers.Header; @@ -13,6 +15,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; +import jakarta.inject.Provider; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; @@ -48,6 +51,7 @@ public class FeedbackDefinitionResource { private final @NonNull FeedbackDefinitionService service; + private final @NonNull Provider requestContext; @GET @Operation(operationId = "findFeedbackDefinitions", summary = "Find Feedback definitions", description = "Find Feedback definitions", responses = { @@ -65,8 +69,13 @@ public Response find( .type(type) .build(); + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Find feedback definitions by '{}' on workspaceId '{}'", criteria, workspaceId); + Page> definitionPage = service.find(page, size, criteria); + log.info("Found feedback definitions by '{}' on workspaceId '{}'", criteria, workspaceId); + return Response.ok() - .entity(service.find(page, size, criteria)) + .entity(definitionPage) .build(); } @@ -77,7 +86,13 @@ public Response find( }) @JsonView({FeedbackDefinition.View.Public.class}) public Response getById(@PathParam("id") @NotNull UUID id) { - return Response.ok().entity(service.get(id)).build(); + + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Get feedback definition by id '{}' on workspaceId '{}'", id, workspaceId); + FeedbackDefinition feedbackDefinition = service.get(id); + log.info("Got feedback definition by id '{}' on workspaceId '{}'", id, workspaceId); + + return Response.ok().entity(feedbackDefinition).build(); } @POST @@ -90,8 +105,14 @@ public Response create( FeedbackDefinition.View.Create.class}) @NotNull @Valid FeedbackDefinition feedbackDefinition, @Context UriInfo uriInfo) { - final var createdFeedbackDefinitions = service.create(feedbackDefinition); - final var uri = uriInfo.getAbsolutePathBuilder().path("/%s".formatted(createdFeedbackDefinitions.getId())) + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Creating feedback definition with id '{}', name '{}' on workspaceId '{}'", feedbackDefinition.getId(), + feedbackDefinition.getName(), workspaceId); + FeedbackDefinition createdFeedbackDefinition = service.create(feedbackDefinition); + log.info("Created feedback definition with id '{}', name '{}' on workspaceId '{}'", + createdFeedbackDefinition.getId(), createdFeedbackDefinition.getName(), workspaceId); + + var uri = uriInfo.getAbsolutePathBuilder().path("/%s".formatted(createdFeedbackDefinition.getId())) .build(); return Response.created(uri).build(); @@ -106,7 +127,13 @@ public Response update(final @PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = FeedbackDefinition.class))) @JsonView({ FeedbackDefinition.View.Update.class}) @NotNull @Valid FeedbackDefinition feedbackDefinition) { + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Updating feedback definition with id '{}' on workspaceId '{}'", feedbackDefinition.getId(), + workspaceId); service.update(id, feedbackDefinition); + log.info("Updated feedback definition with id '{}' on workspaceId '{}'", feedbackDefinition.getId(), + workspaceId); + return Response.noContent().build(); } @@ -117,13 +144,12 @@ public Response update(final @PathParam("id") UUID id, }) public Response deleteById(@PathParam("id") UUID id) { - var workspace = service.getWorkspaceId(id); - - if (workspace == null) { - return Response.noContent().build(); - } + String workspaceId = requestContext.get().getWorkspaceId(); + log.info("Deleting feedback definition by id '{}' on workspaceId '{}'", id, workspaceId); service.delete(id); + log.info("Deleted feedback definition by id '{}' on workspaceId '{}'", id, workspaceId); + return Response.noContent().build(); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/ProjectsResource.java b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/ProjectsResource.java index 79376e4a5..f6e05d4b9 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/ProjectsResource.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/ProjectsResource.java @@ -1,11 +1,13 @@ package com.comet.opik.api.resources.v1.priv; import com.codahale.metrics.annotation.Timed; +import com.comet.opik.api.Page; import com.comet.opik.api.Project; import com.comet.opik.api.ProjectCriteria; import com.comet.opik.api.ProjectUpdate; import com.comet.opik.api.error.ErrorMessage; import com.comet.opik.domain.ProjectService; +import com.comet.opik.infrastructure.auth.RequestContext; import com.fasterxml.jackson.annotation.JsonView; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.headers.Header; @@ -15,6 +17,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; +import jakarta.inject.Provider; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.ws.rs.Consumes; @@ -47,6 +50,7 @@ public class ProjectsResource { private final @NonNull ProjectService projectService; + private final @NonNull Provider requestContext; @GET @Operation(operationId = "findProjects", summary = "Find projects", description = "Find projects", responses = { @@ -62,7 +66,13 @@ public Response find( .projectName(name) .build(); - return Response.ok().entity(projectService.find(page, size, criteria)).build(); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Find projects by '{}' on workspaceId '{}'", criteria, workspaceId); + Page projectPage = projectService.find(page, size, criteria); + log.info("Found projects by '{}', count '{}' on workspaceId '{}'", criteria, projectPage.size(), workspaceId); + + return Response.ok().entity(projectPage).build(); } @GET @@ -71,7 +81,16 @@ public Response find( @ApiResponse(responseCode = "200", description = "Project resource", content = @Content(schema = @Schema(implementation = Project.class)))}) @JsonView({Project.View.Public.class}) public Response getById(@PathParam("id") UUID id) { - return Response.ok().entity(projectService.get(id)).build(); + + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Getting project by id '{}' on workspace_id '{}'", id, workspaceId); + + Project project = projectService.get(id); + + log.info("Got project by id '{}' on workspace_id '{}'", id, workspaceId); + + return Response.ok().entity(project).build(); } @POST @@ -85,8 +104,15 @@ public Response create( @RequestBody(content = @Content(schema = @Schema(implementation = Project.class))) @JsonView(Project.View.Write.class) @Valid Project project, @Context UriInfo uriInfo) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Creating project with name '{}', on workspace_id '{}'", project.name(), workspaceId); + var projectId = projectService.create(project).id(); + log.info("Created project with name '{}', id '{}', on workspace_id '{}'", project.name(), projectId, + workspaceId); + var uri = uriInfo.getAbsolutePathBuilder().path("/%s".formatted(projectId)).build(); return Response.created(uri).build(); @@ -102,7 +128,12 @@ public Response create( public Response update(@PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = ProjectUpdate.class))) @Valid ProjectUpdate project) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Updating project with id '{}' on workspaceId '{}'", id, workspaceId); projectService.update(id, project); + log.info("Updated project with id '{}' on workspaceId '{}'", id, workspaceId); + return Response.noContent().build(); } @@ -114,7 +145,11 @@ public Response update(@PathParam("id") UUID id, }) public Response deleteById(@PathParam("id") UUID id) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Deleting project by id '{}' on workspaceId '{}'", id, workspaceId); projectService.delete(id); + log.info("Deleted project by id '{}' on workspaceId '{}'", id, workspaceId); return Response.noContent().build(); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/SpansResource.java b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/SpansResource.java index e337363f7..968bd9d87 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/SpansResource.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/SpansResource.java @@ -51,6 +51,8 @@ import java.util.UUID; import java.util.stream.Collectors; +import static com.comet.opik.api.Span.SpanPage; +import static com.comet.opik.api.Span.View; import static com.comet.opik.utils.AsyncUtils.setRequestContext; import static com.comet.opik.utils.ValidationUtils.validateProjectNameAndProjectId; @@ -70,8 +72,8 @@ public class SpansResource { @GET @Operation(operationId = "getSpansByProject", summary = "Get spans by project_name or project_id and optionally by trace_id and/or type", description = "Get spans by project_name or project_id and optionally by trace_id and/or type", responses = { - @ApiResponse(responseCode = "200", description = "Spans resource", content = @Content(schema = @Schema(implementation = Span.SpanPage.class)))}) - @JsonView(Span.View.Public.class) + @ApiResponse(responseCode = "200", description = "Spans resource", content = @Content(schema = @Schema(implementation = SpanPage.class)))}) + @JsonView(View.Public.class) public Response getByProjectId( @QueryParam("page") @Min(1) @DefaultValue("1") int page, @QueryParam("size") @Min(1) @DefaultValue("10") int size, @@ -91,11 +93,13 @@ public Response getByProjectId( .filters(spanFilters) .build(); - log.info("Get spans by '{}'", spanSearchCriteria); - var spans = spanService.find(page, size, spanSearchCriteria) + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Get spans by '{}' on workspaceId '{}'", spanSearchCriteria, workspaceId); + SpanPage spans = spanService.find(page, size, spanSearchCriteria) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); - log.info("Found spans by '{}', count '{}'", spanSearchCriteria, spans.size()); + log.info("Found spans by '{}', count '{}' on workspaceId '{}'", spanSearchCriteria, spans.size(), workspaceId); return Response.ok().entity(spans).build(); } @@ -104,15 +108,18 @@ public Response getByProjectId( @Operation(operationId = "getSpanById", summary = "Get span by id", description = "Get span by id", responses = { @ApiResponse(responseCode = "200", description = "Span resource", content = @Content(schema = @Schema(implementation = Span.class))), @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = Span.class)))}) - @JsonView(Span.View.Public.class) + @JsonView(View.Public.class) public Response getById(@PathParam("id") @NotNull UUID id) { - log.info("Getting span by id '{}'", id); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Getting span by id '{}' on workspace_id '{}'", id, workspaceId); var span = spanService.getById(id) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); - log.info("Got span by id '{}', traceId '{}', parentSpanId '{}'", span.id(), span.traceId(), - span.parentSpanId()); + log.info("Got span by id '{}', traceId '{}', parentSpanId '{}' on workspace_id '{}'", span.id(), span.traceId(), + span.parentSpanId(), workspaceId); + return Response.ok().entity(span).build(); } @@ -121,17 +128,20 @@ public Response getById(@PathParam("id") @NotNull UUID id) { @ApiResponse(responseCode = "201", description = "Created", headers = { @Header(name = "Location", required = true, example = "${basePath}/v1/private/spans/{spanId}", schema = @Schema(implementation = String.class))})}) public Response create( - @RequestBody(content = @Content(schema = @Schema(implementation = Span.class))) @JsonView(Span.View.Write.class) @NotNull @Valid Span span, + @RequestBody(content = @Content(schema = @Schema(implementation = Span.class))) @JsonView(View.Write.class) @NotNull @Valid Span span, @Context UriInfo uriInfo) { - log.info("Creating span with id '{}', projectId '{}', traceId '{}', parentSpanId '{}'", - span.id(), span.projectId(), span.traceId(), span.parentSpanId()); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Creating span with id '{}', projectName '{}', traceId '{}', parentSpanId '{}' on workspace_id '{}'", + span.id(), span.projectName(), span.traceId(), span.parentSpanId(), workspaceId); + var id = spanService.create(span) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); var uri = uriInfo.getAbsolutePathBuilder().path("/%s".formatted(id)).build(); - log.info("Created span with id '{}', projectId '{}', traceId '{}', parentSpanId '{}', workspaceId '{}'", - id, span.projectId(), span.traceId(), span.parentSpanId(), requestContext.get().getWorkspaceId()); + log.info("Created span with id '{}', projectName '{}', traceId '{}', parentSpanId '{}' on workspaceId '{}'", + id, span.projectName(), span.traceId(), span.parentSpanId(), workspaceId); return Response.created(uri).build(); } @@ -167,11 +177,13 @@ public Response createSpans( public Response update(@PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = SpanUpdate.class))) @NotNull @Valid SpanUpdate spanUpdate) { - log.info("Updating span with id '{}'", id); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Updating span with id '{}' on workspaceId '{}'", id, workspaceId); spanService.update(id, spanUpdate) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); - log.info("Updated span with id '{}'", id); + log.info("Updated span with id '{}' on workspaceId '{}'", id, workspaceId); return Response.noContent().build(); } @@ -182,7 +194,7 @@ public Response update(@PathParam("id") UUID id, @ApiResponse(responseCode = "204", description = "No Content")}) public Response deleteById(@PathParam("id") @NotNull String id) { - log.info("Deleting span with id '{}'", id); + log.info("Deleting span with id '{}' on workspaceId '{}'", id, requestContext.get().getWorkspaceId()); return Response.status(501).build(); } @@ -193,11 +205,13 @@ public Response deleteById(@PathParam("id") @NotNull String id) { public Response addSpanFeedbackScore(@PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = FeedbackScore.class))) @NotNull @Valid FeedbackScore score) { - log.info("Add span feedback score '{}' for id '{}'", score.name(), id); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Add span feedback score '{}' for id '{}' on workspaceId '{}'", score.name(), id, workspaceId); feedbackScoreService.scoreSpan(id, score) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); - log.info("Added span feedback score '{}' for id '{}'", score.name(), id); + log.info("Added span feedback score '{}' for id '{}' on workspaceId '{}'", score.name(), id, workspaceId); return Response.noContent().build(); } @@ -209,11 +223,13 @@ public Response addSpanFeedbackScore(@PathParam("id") UUID id, public Response deleteSpanFeedbackScore(@PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = DeleteFeedbackScore.class))) @NotNull @Valid DeleteFeedbackScore score) { - log.info("Delete span feedback score '{}' for id '{}'", score.name(), id); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Delete span feedback score '{}' for id '{}' on workspaceId '{}'", score.name(), id, workspaceId); feedbackScoreService.deleteSpanScore(id, score.name()) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); - log.info("Deleted span feedback score '{}' for id '{}'", score.name(), id); + log.info("Deleted span feedback score '{}' for id '{}' on workspaceId '{}'", score.name(), id, workspaceId); return Response.noContent().build(); } @@ -224,12 +240,14 @@ public Response deleteSpanFeedbackScore(@PathParam("id") UUID id, public Response scoreBatchOfSpans( @RequestBody(content = @Content(schema = @Schema(implementation = FeedbackScoreBatch.class))) @NotNull @Valid FeedbackScoreBatch batch) { - log.info("Score batch of spans"); + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Feedback scores batch for spans, size {} on workspaceId '{}'", batch.scores().size(), workspaceId); feedbackScoreService.scoreBatchOfSpans(batch.scores()) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .retryWhen(AsyncUtils.handleConnectionError()) .block(); - log.info("Scored batch of spans"); + log.info("Scored batch for spans, size {} on workspaceId '{}'", batch.scores().size(), workspaceId); return Response.noContent().build(); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/TraceResource.java b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/TraceResource.java index bd1c77aca..31bf42715 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/TraceResource.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/api/resources/v1/priv/TraceResource.java @@ -5,6 +5,7 @@ import com.comet.opik.api.FeedbackScore; import com.comet.opik.api.FeedbackScoreBatch; import com.comet.opik.api.Trace; +import com.comet.opik.api.Trace.TracePage; import com.comet.opik.api.TraceBatch; import com.comet.opik.api.TraceSearchCriteria; import com.comet.opik.api.TraceUpdate; @@ -68,7 +69,7 @@ public class TraceResource { @GET @Operation(operationId = "getTracesByProject", summary = "Get traces by project_name or project_id", description = "Get traces by project_name or project_id", responses = { - @ApiResponse(responseCode = "200", description = "Trace resource", content = @Content(schema = @Schema(implementation = Trace.TracePage.class)))}) + @ApiResponse(responseCode = "200", description = "Trace resource", content = @Content(schema = @Schema(implementation = TracePage.class)))}) @JsonView(Trace.View.Public.class) public Response getByProjectId( @QueryParam("page") @Min(1) @DefaultValue("1") int page, @@ -84,10 +85,18 @@ public Response getByProjectId( .projectId(projectId) .filters(traceFilters) .build(); - return service.find(page, size, searchCriteria) - .map(tracesPage -> Response.ok(tracesPage).build()) + + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Get traces by '{}' on workspaceId '{}'", searchCriteria, workspaceId); + + TracePage tracePage = service.find(page, size, searchCriteria) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); + + log.info("Found traces by '{}', count '{}' on workspaceId '{}'", searchCriteria, tracePage.size(), workspaceId); + + return Response.ok(tracePage).build(); } @GET @@ -97,10 +106,18 @@ public Response getByProjectId( @JsonView(Trace.View.Public.class) public Response getById(@PathParam("id") UUID id) { - return service.get(id) - .map(trace -> Response.ok(trace).build()) + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Getting trace by id '{}' on workspace_id '{}'", id, workspaceId); + + Trace trace = service.get(id) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); + + log.info("Got trace by id '{}', projectId '{}' on workspace_id '{}'", trace.id(), trace.projectId(), + workspaceId); + + return Response.ok(trace).build(); } @POST @@ -111,10 +128,20 @@ public Response create( @RequestBody(content = @Content(schema = @Schema(implementation = Trace.class))) @JsonView(Trace.View.Write.class) @NotNull @Valid Trace trace, @Context UriInfo uriInfo) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Creating trace with id '{}', projectName '{}' on workspace_id '{}'", + trace.id(), trace.projectName(), workspaceId); + var id = service.create(trace) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); + + log.info("Created trace with id '{}', projectName '{}' on workspace_id '{}'", + id, trace.projectName(), workspaceId); + var uri = uriInfo.getAbsolutePathBuilder().path("/%s".formatted(id)).build(); + return Response.created(uri).build(); } @@ -149,10 +176,16 @@ public Response createSpans( public Response update(@PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = TraceUpdate.class))) @Valid @NonNull TraceUpdate trace) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Updating span with id '{}' on workspaceId '{}'", id, workspaceId); + service.update(trace, id) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); + log.info("Updated span with id '{}' on workspaceId '{}'", id, workspaceId); + return Response.noContent().build(); } @@ -162,10 +195,14 @@ public Response update(@PathParam("id") UUID id, @ApiResponse(responseCode = "204", description = "No Content")}) public Response deleteById(@PathParam("id") UUID id) { + log.info("Deleting trace with id '{}'", id); + service.delete(id) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); + log.info("Deleted trace with id '{}'", id); + return Response.noContent().build(); } @@ -176,10 +213,16 @@ public Response deleteById(@PathParam("id") UUID id) { public Response addTraceFeedbackScore(@PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = FeedbackScore.class))) @NotNull @Valid FeedbackScore score) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Add span feedback score '{}' for id '{}' on workspaceId '{}'", score.name(), id, workspaceId); + feedbackScoreService.scoreTrace(id, score) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); + log.info("Added span feedback score '{}' for id '{}' on workspaceId '{}'", score.name(), id, workspaceId); + return Response.noContent().build(); } @@ -190,10 +233,16 @@ public Response addTraceFeedbackScore(@PathParam("id") UUID id, public Response deleteTraceFeedbackScore(@PathParam("id") UUID id, @RequestBody(content = @Content(schema = @Schema(implementation = DeleteFeedbackScore.class))) @NotNull @Valid DeleteFeedbackScore score) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Delete span feedback score '{}' for id '{}' on workspaceId '{}'", score.name(), id, workspaceId); + feedbackScoreService.deleteTraceScore(id, score.name()) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .block(); + log.info("Deleted span feedback score '{}' for id '{}' on workspaceId '{}'", score.name(), id, workspaceId); + return Response.noContent().build(); } @@ -204,11 +253,17 @@ public Response deleteTraceFeedbackScore(@PathParam("id") UUID id, public Response scoreBatchOfTraces( @RequestBody(content = @Content(schema = @Schema(implementation = FeedbackScoreBatch.class))) @NotNull @Valid FeedbackScoreBatch batch) { + String workspaceId = requestContext.get().getWorkspaceId(); + + log.info("Feedback scores batch for traces, size {} on workspaceId '{}'", batch.scores().size(), workspaceId); + feedbackScoreService.scoreBatchOfTraces(batch.scores()) .contextWrite(ctx -> setRequestContext(ctx, requestContext)) .retryWhen(AsyncUtils.handleConnectionError()) .block(); + log.info("Feedback scores batch for traces, size {} on workspaceId '{}'", batch.scores().size(), workspaceId); + return Response.noContent().build(); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetDAO.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetDAO.java index 714b9738c..c3fc5a9c6 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetDAO.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetDAO.java @@ -76,7 +76,4 @@ List find(@Bind("limit") int limit, @SqlQuery("SELECT * FROM datasets WHERE workspace_id = :workspace_id AND name = :name") Optional findByName(@Bind("workspace_id") String workspaceId, @Bind("name") String name); - @SqlQuery("SELECT workspace_id FROM datasets WHERE id = :id") - String getWorkspaceId(@Bind("id") UUID id); - } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetItemService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetItemService.java index f35048b4d..da5b70fee 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetItemService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetItemService.java @@ -82,7 +82,7 @@ private Mono getDatasetId(DatasetItemBatch batch) { Dataset dataset = datasetService.findById(batch.datasetId(), workspaceId); if (dataset == null) { - throw throwsConflict( + throw newConflict( "workspace_name from dataset item batch and dataset_id from item does not match"); } @@ -92,10 +92,12 @@ private Mono getDatasetId(DatasetItemBatch batch) { } private Throwable failWithError(String error) { + log.info(error); return new ClientErrorException(Response.status(422).entity(new ErrorMessage(List.of(error))).build()); } - private ClientErrorException throwsConflict(String error) { + private ClientErrorException newConflict(String error) { + log.info(error); return new ClientErrorException(Response.status(409).entity(new ErrorMessage(List.of(error))).build()); } @@ -172,10 +174,12 @@ private List addIdIfAbsent(DatasetItemBatch batch) { } private Mono failWithConflict(String message) { + log.info(message); return Mono.error(new IdentifierMismatchException(new ErrorMessage(List.of(message)))); } private NotFoundException failWithNotFound(String message) { + log.info(message); return new NotFoundException(message, Response.status(Response.Status.NOT_FOUND).entity(new ErrorMessage(List.of(message))).build()); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetService.java index c0b5c610c..041be369a 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/DatasetService.java @@ -55,8 +55,6 @@ public interface DatasetService { void delete(UUID id); DatasetPage find(int page, int size, DatasetCriteria criteria); - - String getWorkspaceId(UUID id); } @Singleton @@ -64,6 +62,8 @@ public interface DatasetService { @Slf4j class DatasetServiceImpl implements DatasetService { + private static final String DATASET_ALREADY_EXISTS = "Dataset already exists"; + private final @NonNull IdGenerator idGenerator; private final @NonNull TransactionTemplate template; private final @NonNull Provider requestContext; @@ -95,7 +95,8 @@ public Dataset save(@NonNull Dataset dataset) { return dao.findById(newDataset.id(), workspaceId).orElseThrow(); } catch (UnableToExecuteStatementException e) { if (e.getCause() instanceof SQLIntegrityConstraintViolationException) { - throw new EntityAlreadyExistsException(new ErrorMessage(List.of("Dataset already exists"))); + log.info(DATASET_ALREADY_EXISTS); + throw new EntityAlreadyExistsException(new ErrorMessage(List.of(DATASET_ALREADY_EXISTS))); } else { throw e; } @@ -110,6 +111,7 @@ public UUID getOrCreate(@NonNull String workspaceId, @NonNull String name, @NonN if (dataset.isEmpty()) { UUID id = idGenerator.generateId(); + log.info("Creating dataset with id '{}', name '{}', workspaceId '{}'", id, name, workspaceId); template.inTransaction(WRITE, handle -> { handle.attach(DatasetDAO.class) .save( @@ -125,6 +127,7 @@ public UUID getOrCreate(@NonNull String workspaceId, @NonNull String name, @NonN log.info("Created dataset with id '{}', name '{}', workspaceId '{}'", id, name, workspaceId); return id; } + UUID id = dataset.get().id(); log.info("Got dataset with id '{}', name '{}', workspaceId '{}'", id, name, workspaceId); return id; @@ -146,7 +149,8 @@ public void update(@NonNull UUID id, @NonNull DatasetUpdate dataset) { } } catch (UnableToExecuteStatementException e) { if (e.getCause() instanceof SQLIntegrityConstraintViolationException) { - throw new EntityAlreadyExistsException(new ErrorMessage(List.of("Dataset already exists"))); + log.info(DATASET_ALREADY_EXISTS); + throw new EntityAlreadyExistsException(new ErrorMessage(List.of(DATASET_ALREADY_EXISTS))); } else { throw e; } @@ -226,6 +230,7 @@ public void delete(@NonNull DatasetIdentifier identifier) { private NotFoundException newNotFoundException() { String message = "Dataset not found"; + log.info(message); return new NotFoundException(message, Response.status(Response.Status.NOT_FOUND).entity(new ErrorMessage(List.of(message))).build()); } @@ -273,10 +278,4 @@ public DatasetPage find(int page, int size, DatasetCriteria criteria) { .toList(), page, datasets.size(), count); }); } - - @Override - public String getWorkspaceId(UUID id) { - return template.inTransaction(READ_ONLY, handle -> handle.attach(DatasetDAO.class).getWorkspaceId(id)); - } - } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentItemService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentItemService.java index 51e15d26e..0967e2579 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentItemService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentItemService.java @@ -27,7 +27,7 @@ public class ExperimentItemService { private final @NonNull ExperimentService experimentService; private final @NonNull DatasetItemDAO datasetItemDAO; - public Mono create(Set experimentItems) { + public Mono create(@NonNull Set experimentItems) { Preconditions.checkArgument(CollectionUtils.isNotEmpty(experimentItems), "Argument 'experimentItems' must not be empty"); @@ -69,12 +69,15 @@ private void validateExperimentsWorkspace(Set experimentItems, S .block()); if (!allExperimentsBelongToWorkspace) { - throw new ClientErrorException( - "Upserting experiment item with 'experiment_id' not belonging to the workspace", - Response.Status.CONFLICT); + throw createConflict("Upserting experiment item with 'experiment_id' not belonging to the workspace"); } } + private ClientErrorException createConflict(String message) { + log.info(message); + return new ClientErrorException(message, Response.Status.CONFLICT); + } + private void validateDatasetItemsWorkspace(Set experimentItems, String workspaceId) { Set datasetItemIds = experimentItems .stream() @@ -87,9 +90,7 @@ private void validateDatasetItemsWorkspace(Set experimentItems, .block()); if (!allDatasetItemsBelongToWorkspace) { - throw new ClientErrorException( - "Upserting experiment item with 'dataset_item_id' not belonging to the workspace", - Response.Status.CONFLICT); + throw createConflict("Upserting experiment item with 'dataset_item_id' not belonging to the workspace"); } } @@ -110,7 +111,9 @@ public Mono get(@NonNull UUID id) { } private NotFoundException newNotFoundException(UUID id) { - return new NotFoundException("Not found experiment item with id '%s'".formatted(id)); + String message = "Not found experiment item with id '%s'".formatted(id); + log.info(message); + return new NotFoundException(message); } public Mono delete(@NonNull Set ids) { diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentService.java index 7383c141f..6cb15f727 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/ExperimentService.java @@ -117,12 +117,15 @@ private Mono handleCreateError(Throwable throwable, UUID id) { } private ClientErrorException newConflictException(UUID id) { - return new ClientErrorException("Already exists experiment with id '%s'".formatted(id), - Response.Status.CONFLICT); + String message = "Already exists experiment with id '%s'".formatted(id); + log.info(message); + return new ClientErrorException(message, Response.Status.CONFLICT); } private NotFoundException newNotFoundException(UUID id) { - return new NotFoundException("Not found experiment with id '%s'".formatted(id)); + String message = "Not found experiment with id '%s'".formatted(id); + log.info(message); + return new NotFoundException(message); } public Mono validateExperimentWorkspace(@NonNull String workspaceId, @NonNull Set experimentIds) { diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackDefinitionDAO.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackDefinitionDAO.java index a7332c8a9..ecfef9c02 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackDefinitionDAO.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackDefinitionDAO.java @@ -64,7 +64,4 @@ List> find(@Bind("limit") int limit, @Bind("workspaceId") String workspaceId, @Define("name") @Bind("name") String name, @Define("type") @Bind("type") FeedbackType type); - - @SqlQuery("SELECT workspace_id FROM feedback_definitions WHERE id = :id") - String getWorkspaceId(@Bind("id") UUID id); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackDefinitionService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackDefinitionService.java index e7ba34164..afe5e8627 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackDefinitionService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackDefinitionService.java @@ -14,6 +14,7 @@ import jakarta.ws.rs.core.Response; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.jdbi.v3.core.statement.UnableToExecuteStatementException; import ru.vyarus.guicey.jdbi3.tx.TransactionTemplate; @@ -36,14 +37,15 @@ public interface FeedbackDefinitionService { > T get(UUID id); Page> find(int page, int size, FeedbackDefinitionCriteria criteria); - - String getWorkspaceId(UUID id); } +@Slf4j @Singleton @RequiredArgsConstructor(onConstructor_ = @Inject) class FeedbackDefinitionServiceImpl implements FeedbackDefinitionService { + private static final String FEEDBACK_ALREADY_EXISTS = "Feedback already exists"; + private final @NonNull TransactionTemplate template; private final @NonNull IdGenerator generator; private final @NonNull Provider requestContext; @@ -75,14 +77,6 @@ public Page> find(int page, int size, @NonNull FeedbackDef }); } - @Override - public String getWorkspaceId(UUID id) { - return template.inTransaction(READ_ONLY, handle -> { - var repository = handle.attach(FeedbackDefinitionDAO.class); - return repository.getWorkspaceId(id); - }); - } - @Override @SuppressWarnings("unchecked") public > T get(@NonNull UUID id) { @@ -145,7 +139,8 @@ public > T create(@NonNull T feedback) { } catch (UnableToExecuteStatementException e) { if (e.getCause() instanceof SQLIntegrityConstraintViolationException) { - throw new EntityAlreadyExistsException(new ErrorMessage(List.of("Feedback already exists"))); + log.info(FEEDBACK_ALREADY_EXISTS); + throw new EntityAlreadyExistsException(new ErrorMessage(List.of(FEEDBACK_ALREADY_EXISTS))); } else { throw e; } @@ -181,7 +176,8 @@ public > T update(@NonNull UUID id, @NonNull }); } catch (UnableToExecuteStatementException e) { if (e.getCause() instanceof SQLIntegrityConstraintViolationException) { - throw new EntityAlreadyExistsException(new ErrorMessage(List.of("Feedback already exists"))); + log.info(FEEDBACK_ALREADY_EXISTS); + throw new EntityAlreadyExistsException(new ErrorMessage(List.of(FEEDBACK_ALREADY_EXISTS))); } else { throw e; } @@ -201,6 +197,7 @@ public void delete(@NonNull UUID id) { private NotFoundException createNotFoundError() { String message = "Feedback definition not found"; + log.info(message); return new NotFoundException(message, Response.status(Response.Status.NOT_FOUND).entity(new ErrorMessage(List.of(message))).build()); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackScoreService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackScoreService.java index 6cb1084da..3c2569ba6 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackScoreService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/FeedbackScoreService.java @@ -260,6 +260,7 @@ public Mono deleteTraceScore(UUID id, String name) { } private Mono failWithNotFound(String errorMessage) { + log.info(errorMessage); return Mono.error(new NotFoundException(Response.status(404) .entity(new ErrorMessage(List.of(errorMessage))).build())); } @@ -269,12 +270,16 @@ private Mono extractResult(Long rowsUpdated) { } private Throwable failWithTraceNotFound(UUID id) { + String message = "Trace id: %s not found".formatted(id); + log.info(message); return new NotFoundException(Response.status(404) - .entity(new ErrorMessage(List.of("Trace id: %s not found".formatted(id)))).build()); + .entity(new ErrorMessage(List.of(message))).build()); } private NotFoundException failWithSpanNotFound(UUID id) { - return new NotFoundException("Not found span with id '%s'".formatted(id)); + String message = "Not found span with id '%s'".formatted(id); + log.info(message); + return new NotFoundException(message); } } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/ProjectDAO.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/ProjectDAO.java index dc3ad537c..fdfb23685 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/ProjectDAO.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/ProjectDAO.java @@ -70,7 +70,4 @@ default Optional fetch(UUID id, String workspaceId) { @SqlQuery("SELECT * FROM projects WHERE workspace_id = :workspaceId AND name IN ()") List findByNames(@Bind("workspaceId") String workspaceId, @BindList("names") Collection names); - - @SqlQuery("SELECT workspace_id FROM projects WHERE id = :id") - String getWorkspaceId(@Bind("id") UUID id); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/ProjectService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/ProjectService.java index 2ad62fd02..043d6e5a5 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/ProjectService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/ProjectService.java @@ -16,6 +16,7 @@ import jakarta.ws.rs.core.Response; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.jdbi.v3.core.statement.UnableToExecuteStatementException; import ru.vyarus.guicey.jdbi3.tx.TransactionTemplate; @@ -50,20 +51,21 @@ public interface ProjectService { List findByNames(String workspaceId, List names); Project getOrCreate(String workspaceId, String projectName, String userName); - - String getWorkspaceId(UUID id); } +@Slf4j @Singleton @RequiredArgsConstructor(onConstructor_ = @Inject) class ProjectServiceImpl implements ProjectService { + private static final String PROJECT_ALREADY_EXISTS = "Project already exists"; private final @NonNull TransactionTemplate template; private final @NonNull IdGenerator idGenerator; private final @NonNull Provider requestContext; - private static NotFoundException createNotFoundError() { + private NotFoundException createNotFoundError() { String message = "Project not found"; + log.info(message); return new NotFoundException(message, Response.status(Response.Status.NOT_FOUND).entity(new ErrorMessage(List.of(message))).build()); } @@ -97,13 +99,18 @@ private Project createProject(Project project, UUID projectId, String userName, }); } catch (UnableToExecuteStatementException e) { if (e.getCause() instanceof SQLIntegrityConstraintViolationException) { - throw new EntityAlreadyExistsException(new ErrorMessage(List.of("Project already exists"))); + throw newConflict(); } else { throw e; } } } + private EntityAlreadyExistsException newConflict() { + log.info(PROJECT_ALREADY_EXISTS); + return new EntityAlreadyExistsException(new ErrorMessage(List.of(PROJECT_ALREADY_EXISTS))); + } + @Override public Project update(@NonNull UUID id, @NonNull ProjectUpdate projectUpdate) { String userName = requestContext.get().getUserName(); @@ -115,7 +122,7 @@ public Project update(@NonNull UUID id, @NonNull ProjectUpdate projectUpdate) { var repository = handle.attach(ProjectDAO.class); Project project = repository.fetch(id, workspaceId) - .orElseThrow(ProjectServiceImpl::createNotFoundError); + .orElseThrow(this::createNotFoundError); repository.update(project.id(), workspaceId, @@ -128,7 +135,7 @@ public Project update(@NonNull UUID id, @NonNull ProjectUpdate projectUpdate) { } catch (UnableToExecuteStatementException e) { if (e.getCause() instanceof SQLIntegrityConstraintViolationException) { - throw new EntityAlreadyExistsException(new ErrorMessage(List.of("Project already exists"))); + throw newConflict(); } else { throw e; } @@ -148,7 +155,7 @@ public Project get(@NonNull UUID id, @NonNull String workspaceId) { var repository = handle.attach(ProjectDAO.class); - return repository.fetch(id, workspaceId).orElseThrow(ProjectServiceImpl::createNotFoundError); + return repository.fetch(id, workspaceId).orElseThrow(this::createNotFoundError); }); } @@ -168,6 +175,7 @@ public void delete(@NonNull UUID id) { if (project.get().name().equalsIgnoreCase(DEFAULT_PROJECT)) { var message = "Cannot delete default project"; + log.info(message); throw new CannotDeleteProjectException(new ErrorMessage(List.of(message))); } @@ -214,25 +222,22 @@ public List findByNames(String workspaceId, List names) { public Project getOrCreate(@NonNull String workspaceId, @NonNull String projectName, @NonNull String userName) { return findByNames(workspaceId, List.of(projectName)) - .stream().findFirst() + .stream() + .findFirst() .orElseGet(() -> { + log.info("Creating project with name '{}' on workspaceId '{}'", projectName, workspaceId); var project = Project.builder() .name(projectName) .build(); - var projectId = idGenerator.generateId(); + UUID projectId = idGenerator.generateId(); - return createProject(project, projectId, userName, workspaceId); + project = createProject(project, projectId, userName, workspaceId); + + log.info("Created project with id '{}', name '{}' on workspaceId '{}'", projectId, projectName, + workspaceId); + return project; }); } - @Override - public String getWorkspaceId(UUID id) { - return template.inTransaction(READ_ONLY, handle -> { - - var repository = handle.attach(ProjectDAO.class); - - return repository.getWorkspaceId(id); - }); - } } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/SpanService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/SpanService.java index 5f0c4fdf4..27caa0053 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/SpanService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/SpanService.java @@ -235,10 +235,13 @@ private Mono updateOrFail(SpanUpdate spanUpdate, UUID id, Span existingSpa } private NotFoundException newNotFoundException(UUID id) { - return new NotFoundException("Not found span with id '%s'".formatted(id)); + String message = "Not found span with id '%s'".formatted(id); + log.info(message); + return new NotFoundException(message); } private Mono failWithConflict(String error) { + log.info(error); return Mono.error(new IdentifierMismatchException(new ErrorMessage(List.of(error)))); } diff --git a/apps/opik-backend/src/main/java/com/comet/opik/domain/TraceService.java b/apps/opik-backend/src/main/java/com/comet/opik/domain/TraceService.java index 77002bf5c..1b3b70a55 100644 --- a/apps/opik-backend/src/main/java/com/comet/opik/domain/TraceService.java +++ b/apps/opik-backend/src/main/java/com/comet/opik/domain/TraceService.java @@ -22,6 +22,7 @@ import jakarta.ws.rs.core.Response; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -55,6 +56,7 @@ public interface TraceService { } +@Slf4j @Singleton @RequiredArgsConstructor(onConstructor_ = @Inject) class TraceServiceImpl implements TraceService { @@ -251,10 +253,12 @@ private Mono getProjectByName(String projectName) { } private Mono failWithConflict(String error) { + log.info(error); return Mono.error(new IdentifierMismatchException(new ErrorMessage(List.of(error)))); } private NotFoundException failWithNotFound(String error) { + log.info(error); return new NotFoundException(Response.status(404).entity(new ErrorMessage(List.of(error))).build()); } From 938bd9958204c6f0bf728d439f573e2cc20c2c69 Mon Sep 17 00:00:00 2001 From: "andrii.dudar" Date: Mon, 16 Sep 2024 14:16:41 +0200 Subject: [PATCH 4/7] [OPIK-91] [UX improvements] Implement the Experiment configuration section in the compare page (#252) --- .../CompareExperimentsDetails.tsx | 229 ++++++++++++++++++ .../CompareExperimentsPage.tsx | 28 +-- .../ConfigurationTab/CompareConfigCell.tsx | 4 +- .../ConfigurationTab/ConfigurationTab.tsx | 5 +- .../ExperimentItemsTab/ExperimentItemsTab.tsx | 1 + .../DataTableNoData/DataTableNoData.tsx | 5 +- 6 files changed, 248 insertions(+), 24 deletions(-) create mode 100644 apps/opik-frontend/src/components/pages/CompareExperimentsPage/CompareExperimentsDetails.tsx diff --git a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/CompareExperimentsDetails.tsx b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/CompareExperimentsDetails.tsx new file mode 100644 index 000000000..d513e2372 --- /dev/null +++ b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/CompareExperimentsDetails.tsx @@ -0,0 +1,229 @@ +import React, { useEffect, useMemo } from "react"; +import sortBy from "lodash/sortBy"; +import { + ArrowUpRight, + Clock, + Database, + FlaskConical, + Maximize2, + Minimize2, + PenLine, +} from "lucide-react"; + +import useAppStore from "@/store/AppStore"; +import useBreadcrumbsStore from "@/store/BreadcrumbsStore"; +import FeedbackScoreTag from "@/components/shared/FeedbackScoreTag/FeedbackScoreTag"; +import { Experiment } from "@/types/datasets"; +import { TableBody, TableCell, TableRow } from "@/components/ui/table"; +import { Tag } from "@/components/ui/tag"; +import { Link } from "@tanstack/react-router"; +import { formatDate } from "@/lib/date"; +import uniq from "lodash/uniq"; +import isUndefined from "lodash/isUndefined"; +import { Button } from "@/components/ui/button"; +import { BooleanParam, useQueryParam } from "use-query-params"; + +type CompareExperimentsDetailsProps = { + experimentsIds: string[]; + experiments: Experiment[]; +}; + +const CompareExperimentsDetails: React.FunctionComponent< + CompareExperimentsDetailsProps +> = ({ experiments, experimentsIds }) => { + const workspaceName = useAppStore((state) => state.activeWorkspaceName); + const setBreadcrumbParam = useBreadcrumbsStore((state) => state.setParam); + + const isCompare = experimentsIds.length > 1; + + const experiment = experiments[0]; + + const title = !isCompare + ? experiment?.name + : `Compare (${experimentsIds.length})`; + + const [showCompareFeedback = false, setShowCompareFeedback] = useQueryParam( + "scoreTable", + BooleanParam, + { + updateType: "replaceIn", + }, + ); + + useEffect(() => { + title && setBreadcrumbParam("compare", "compare", title); + return () => setBreadcrumbParam("compare", "compare", ""); + }, [title, setBreadcrumbParam]); + + const scoreMap = useMemo(() => { + return !isCompare + ? {} + : experiments.reduce>>((acc, e) => { + acc[e.id] = (e.feedback_scores || [])?.reduce>( + (a, f) => { + a[f.name] = f.value; + return a; + }, + {}, + ); + + return acc; + }, {}); + }, [isCompare, experiments]); + + const scoreColumns = useMemo(() => { + return uniq( + Object.values(scoreMap).reduce( + (acc, m) => acc.concat(Object.keys(m)), + [], + ), + ).sort(); + }, [scoreMap]); + + const renderCompareFeedbackScoresButton = () => { + if (!isCompare) return null; + + const text = showCompareFeedback + ? "Collapse feedback scores" + : "Expand feedback scores"; + const Icon = showCompareFeedback ? Minimize2 : Maximize2; + + return ( + + ); + }; + + const renderSubSection = () => { + if (isCompare) { + const tag = + experimentsIds.length === 2 ? ( + + +
{experiments[1]?.name}
+
+ ) : ( + + {`${experimentsIds.length - 1} experiments`} + + ); + + return ( +
+ Baseline of + + +
{experiment?.name}
+
+ compared against + {tag} +
+ ); + } else { + return ( +
+ +
+ {sortBy(experiment?.feedback_scores ?? [], "name").map( + (feedbackScore) => { + return ( + + ); + }, + )} +
+
+ ); + } + }; + + const renderCompareFeedbackScores = () => { + if (!isCompare || !showCompareFeedback) return null; + + return ( +
+ {experiments.length ? ( + + + {experiments.map((e) => ( + + +
+ {e.name} +
+
+ {scoreColumns.map((id) => { + const value = scoreMap[e.id]?.[id]; + + return ( + +
+ {isUndefined(value) ? ( + "–" + ) : ( + + )} +
+
+ ); + })} +
+ ))} +
+
+ ) : ( +
+ No feedback scores for selected experiments +
+ )} +
+ ); + }; + + return ( +
+
+

{title}

+ {renderCompareFeedbackScoresButton()} +
+
+ {!isCompare && ( + + +
{formatDate(experiment?.created_at)}
+
+ )} + + + +
{experiment?.dataset_name}
+ +
+ +
+ {renderSubSection()} + {renderCompareFeedbackScores()} +
+ ); +}; + +export default CompareExperimentsDetails; diff --git a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/CompareExperimentsPage.tsx b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/CompareExperimentsPage.tsx index d2d9c3f51..7f268804b 100644 --- a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/CompareExperimentsPage.tsx +++ b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/CompareExperimentsPage.tsx @@ -1,18 +1,16 @@ -import React, { useEffect } from "react"; +import React from "react"; import isUndefined from "lodash/isUndefined"; import { JsonParam, StringParam, useQueryParam } from "use-query-params"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import CompareExperimentsDetails from "@/components/pages/CompareExperimentsPage/CompareExperimentsDetails"; import ExperimentItemsTab from "@/components/pages/CompareExperimentsPage/ExperimentItemsTab/ExperimentItemsTab"; import ConfigurationTab from "@/components/pages/CompareExperimentsPage/ConfigurationTab/ConfigurationTab"; import useExperimentsByIds from "@/api/datasets/useExperimenstByIds"; -import useBreadcrumbsStore from "@/store/BreadcrumbsStore"; import useDeepMemo from "@/hooks/useDeepMemo"; import { Experiment } from "@/types/datasets"; const CompareExperimentsPage: React.FunctionComponent = () => { - const setBreadcrumbParam = useBreadcrumbsStore((state) => state.setParam); - const [tab = "items", setTab] = useQueryParam("tab", StringParam, { updateType: "replaceIn", }); @@ -21,8 +19,6 @@ const CompareExperimentsPage: React.FunctionComponent = () => { updateType: "replaceIn", }); - const isCompare = experimentsIds.length > 1; - const response = useExperimentsByIds({ experimentsIds, }); @@ -40,22 +36,12 @@ const CompareExperimentsPage: React.FunctionComponent = () => { return experiments ?? []; }, [experiments]); - const experiment = memorizedExperiments[0]; - - const title = !isCompare - ? experiment?.name - : `Compare (${experimentsIds.length})`; - - useEffect(() => { - title && setBreadcrumbParam("compare", "compare", title); - return () => setBreadcrumbParam("compare", "compare", ""); - }, [title, setBreadcrumbParam]); - return ( -
-
-

{title}

-
+
+ diff --git a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ConfigurationTab/CompareConfigCell.tsx b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ConfigurationTab/CompareConfigCell.tsx index b94b240a2..ae5323c24 100644 --- a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ConfigurationTab/CompareConfigCell.tsx +++ b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ConfigurationTab/CompareConfigCell.tsx @@ -26,7 +26,9 @@ const CompareConfigCell: React.FunctionComponent< rowHeightClass: "min-h-14", }} > -
{String(data)}
+
+ {String(data)} +
); }; diff --git a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ConfigurationTab/ConfigurationTab.tsx b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ConfigurationTab/ConfigurationTab.tsx index 3c615adf6..dca0c89c9 100644 --- a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ConfigurationTab/ConfigurationTab.tsx +++ b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ConfigurationTab/ConfigurationTab.tsx @@ -80,6 +80,7 @@ const ConfigurationTab: React.FunctionComponent = ({ header: CompareExperimentsHeader as never, cell: CompareConfigCell as never, size, + minSize: 120, }); }); @@ -146,7 +147,9 @@ const ConfigurationTab: React.FunctionComponent = ({ const noDataText = search ? "No search results" - : "There is no data for the selected experiments"; + : isCompare + ? "These experiments have no configuration" + : "This experiment has no configuration"; const resizeConfig = useMemo( () => ({ diff --git a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ExperimentItemsTab/ExperimentItemsTab.tsx b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ExperimentItemsTab/ExperimentItemsTab.tsx index cb6c41b91..500724634 100644 --- a/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ExperimentItemsTab/ExperimentItemsTab.tsx +++ b/apps/opik-frontend/src/components/pages/CompareExperimentsPage/ExperimentItemsTab/ExperimentItemsTab.tsx @@ -180,6 +180,7 @@ const ExperimentItemsTab: React.FunctionComponent = ({ }, }, size, + minSize: 120, }); }); diff --git a/apps/opik-frontend/src/components/shared/DataTableNoData/DataTableNoData.tsx b/apps/opik-frontend/src/components/shared/DataTableNoData/DataTableNoData.tsx index de79e78f8..aa10bd2d3 100644 --- a/apps/opik-frontend/src/components/shared/DataTableNoData/DataTableNoData.tsx +++ b/apps/opik-frontend/src/components/shared/DataTableNoData/DataTableNoData.tsx @@ -2,14 +2,17 @@ import React from "react"; type DataTableNoDataProps = { title: string; + children?: React.ReactNode; }; const DataTableNoData: React.FunctionComponent = ({ title, + children, }) => { return ( -
+
{title} +
{children}
); }; From 107379af7c4e188415ba7e8e97ba662be31561e1 Mon Sep 17 00:00:00 2001 From: Jacques Verre Date: Mon, 16 Sep 2024 13:44:27 +0100 Subject: [PATCH 5/7] Update docs --- .../documentation/docs/self-host/local_deployment.md | 8 ++++++-- .../documentation/docs/self-host/overview.md | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/opik-documentation/documentation/docs/self-host/local_deployment.md b/apps/opik-documentation/documentation/docs/self-host/local_deployment.md index 71ff131fd..763fcd89d 100644 --- a/apps/opik-documentation/documentation/docs/self-host/local_deployment.md +++ b/apps/opik-documentation/documentation/docs/self-host/local_deployment.md @@ -24,8 +24,8 @@ To install Opik, you will need to clone the Opik repository and run the `docker- # Clone the Opik repository git clone https://github.com/comet-ml/opik.git -# Navigate to the opik directory -cd opik +# Navigate to the opik/deployment/docker-compose directory +cd opik/deployment/docker-compose # Start the Opik platform docker compose up --detach @@ -50,6 +50,10 @@ All the data logged to the Opik platform will be stored in the `~/opik` director ## Starting, stopping and upgrading Opik +:::note +All the `docker compose` commands should be run from the `opik/deployment/docker-compose` directory. +::: + The `docker compose up` command can be used to install, start and upgrade Opik: ```bash diff --git a/apps/opik-documentation/documentation/docs/self-host/overview.md b/apps/opik-documentation/documentation/docs/self-host/overview.md index 21e081e47..21d2fbe75 100644 --- a/apps/opik-documentation/documentation/docs/self-host/overview.md +++ b/apps/opik-documentation/documentation/docs/self-host/overview.md @@ -21,7 +21,7 @@ If you would like to try out Opik locally, we recommend using our Local installa git clone https://github.com/comet-ml/opik.git # Run the Opik platform -cd opik +cd opik/deployment/docker-compose docker compose up --detach # Configure the Python SDK to point to the local Opik platform From 9e9a437cc162245b9a79c4e7d87ad73310fa7d7c Mon Sep 17 00:00:00 2001 From: Jacques Verre Date: Mon, 16 Sep 2024 14:01:00 +0100 Subject: [PATCH 6/7] Removed images --- docker-compose.yaml | 112 --------------------------------------- nginx_default_local.conf | 27 ---------- 2 files changed, 139 deletions(-) delete mode 100644 docker-compose.yaml delete mode 100644 nginx_default_local.conf diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index 31cc50d98..000000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,112 +0,0 @@ -name: opik - -services: - mysql: - image: mysql:8.4.2 - hostname: mysql - environment: - MYSQL_ROOT_PASSWORD: opik - MYSQL_DATABASE: opik - MYSQL_USER: opik - MYSQL_PASSWORD: opik - healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "--silent"] - timeout: 1s - interval: 1s - retries: 300 - ports: - - "3306:3306" - - redis: - image: redis:7.2.4-alpine3.19 - hostname: redis - command: redis-server --requirepass opik - ports: - - '6379:6379' - healthcheck: - test: [ "CMD", "nc", "-z", "localhost", "6379" ] - interval: 2s - timeout: 4s - retries: 20 - start_period: 30s - restart: always - - - clickhouse: - image: clickhouse/clickhouse-server:23.8.15.35-alpine - hostname: clickhouse - environment: - CLICKHOUSE_DB: opik - CLICKHOUSE_USER: opik - CLICKHOUSE_PASSWORD: opik - # Enables SQL-driven Access Control and Account Management: - # https://clickhouse.com/docs/en/operations/access-rights#enabling-access-control - CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1 - ports: - - "8123:8123" # HTTP default port - - "9000:9000" # Native Protocol port - volumes: # Mounted on your $HOME folder - - ~/clickhouse/data:/var/lib/clickhouse/ - - ~/clickhouse/logs:/var/log/clickhouse-server/ - healthcheck: - test: [ "CMD", "wget", "--spider", "-q", "http://127.0.0.1:8123/ping" ] - interval: 1s - timeout: 1s - retries: 300 - - backend: - image: opik-backend - hostname: backend - build: - context: ./apps/opik-backend - dockerfile: Dockerfile - args: - OPIK_VERSION: latest - command: ["bash", "-c", "./run_db_migrations.sh && ./entrypoint.sh"] - environment: - DOCKER_BUILDKIT: 1 - STATE_DB_URL: "jdbc:mysql://mysql:3306/opik?createDatabaseIfNotExist=true&rewriteBatchedStatements=true" - STATE_DB_DATABASE_NAME: opik - STATE_DB_USER: opik - STATE_DB_PASS: opik - ANALYTICS_DB_MIGRATIONS_URL: "jdbc:clickhouse://clickhouse:8123" - ANALYTICS_DB_MIGRATIONS_USER: opik - ANALYTICS_DB_MIGRATIONS_PASS: opik - ANALYTICS_DB_PROTOCOL: "HTTP" - ANALYTICS_DB_HOST: "clickhouse" - ANALYTICS_DB_PORT: 8123 - ANALYTICS_DB_USERNAME: opik - ANALYTICS_DB_DATABASE_NAME: opik - JAVA_OPTS: "-Dliquibase.propertySubstitutionEnabled=true" - REDIS_URL: redis://:opik@redis:6379/ - ANALYTICS_DB_PASS: opik - ports: - - "8080:8080" - - "3003:3003" - depends_on: - mysql: - condition: service_healthy - clickhouse: - condition: service_healthy - - frontend: - image: opik-frontend - hostname: frontend - build: - context: ./apps/opik-frontend - dockerfile: Dockerfile - args: - OPIK_VERSION: latest - ports: - - "5173:5173" - extra_hosts: - - "apihost:host-gateway" - volumes: - - ./nginx_default_local.conf:/etc/nginx/conf.d/default.conf - depends_on: - backend: - condition: service_started - - -networks: - default: \ No newline at end of file diff --git a/nginx_default_local.conf b/nginx_default_local.conf deleted file mode 100644 index 46175df8d..000000000 --- a/nginx_default_local.conf +++ /dev/null @@ -1,27 +0,0 @@ -server { - listen 5173; - server_name localhost; - root /usr/share/nginx/html; - index index.html; - - location /api/ { - rewrite /api/(.*) /$1 break; - proxy_pass http://apihost:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - proxy_read_timeout 90; - proxy_connect_timeout 90; - proxy_send_timeout 90; - - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - - location / { - try_files $uri $uri/ /index.html; - } -} \ No newline at end of file From 3a3c263c9a2c5d49f01b2cc7aa8536d24c409854 Mon Sep 17 00:00:00 2001 From: Jacques Verre Date: Mon, 16 Sep 2024 15:17:24 +0100 Subject: [PATCH 7/7] Update to self-host documentation --- .../docs/self-host/local_deployment.md | 15 +++++++++------ .../documentation/docs/self-host/overview.md | 17 ++++++++++++++--- .../python-sdk-docs/source/index.rst | 10 +++++++++- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/apps/opik-documentation/documentation/docs/self-host/local_deployment.md b/apps/opik-documentation/documentation/docs/self-host/local_deployment.md index 763fcd89d..73b678ef4 100644 --- a/apps/opik-documentation/documentation/docs/self-host/local_deployment.md +++ b/apps/opik-documentation/documentation/docs/self-host/local_deployment.md @@ -34,15 +34,18 @@ docker compose up --detach Opik will now be available at `http://localhost:5173`. :::tip -You will need to make sure that the Opik Python SDK is configured to point to the Opik server you just started. You can use the `login` CLI command provided by the Opik Python SDK: +You will need to make sure that the Opik Python SDK is configured to point to the Opik server you just started. For this, make sure you set the environment variable `OPIK_BASE_URL` to the URL of the Opik server: ```bash -# Install the Opik Python SDK -pip install opik +export OPIK_BASE_URL=http://localhost:5173/api +``` + +or in python: + +```python +import os -# Follow the prompts to create an SDK configuration file that points to -# the Opik server you just started -opik login --local +os.environ["OPIK_BASE_URL"] = "http://localhost:5173/api" ``` ::: diff --git a/apps/opik-documentation/documentation/docs/self-host/overview.md b/apps/opik-documentation/documentation/docs/self-host/overview.md index 21d2fbe75..01aaf34de 100644 --- a/apps/opik-documentation/documentation/docs/self-host/overview.md +++ b/apps/opik-documentation/documentation/docs/self-host/overview.md @@ -24,12 +24,23 @@ git clone https://github.com/comet-ml/opik.git cd opik/deployment/docker-compose docker compose up --detach +``` +``` + +Opik will now be available at `http://localhost:5173` and all traces logged from your local machine will be logged to this local Opik instance. In order for traces and other data to be logged to your Opik instance, you need to make sure that the Opik Python SDK is configured to point to the Opik server you just started. You can do this by running the following command: + +```bash # Configure the Python SDK to point to the local Opik platform -pip install opik -opik login --local +export OPIK_BASE_URL=http://localhost:5173/api ``` -Opik will now be available at `http://localhost:5173` and all traces logged from your local machine will be logged to this local Opik instance. +or in Python: + +```python +import os + +os.environ["OPIK_BASE_URL"] = "http://localhost:5173/api" +``` To learn more about how to manage you local Opik deployment, you can refer to our [local deployment guide](./local_deployment.md). diff --git a/apps/opik-documentation/python-sdk-docs/source/index.rst b/apps/opik-documentation/python-sdk-docs/source/index.rst index 86e3d923d..694a405dc 100644 --- a/apps/opik-documentation/python-sdk-docs/source/index.rst +++ b/apps/opik-documentation/python-sdk-docs/source/index.rst @@ -23,7 +23,15 @@ To get start with the package, you can install it using pip:: pip install opik -To finish configuring the Opik Python SDK, you recommend running the `opik login` command. +To finish configuring the Opik Python SDK, you will need to set the environment variables: + +- If you are using the Comet managed Opik platform: + + - `OPIK_API_KEY`: The API key to the Opik platform. + - `OPIK_WORKSPACE`: The workspace to log traces to, this is often the same as your Opik username. +- If you are using a self-hosted Opik platform: + + - `OPIK_BASE_URL`: The base URL of the Opik platform. ============= Using the SDK