# Agent Hub

This project is a cookiecutter template that instantiates a webapp for DSS.

## Template Content

- **.github/**: Contains static checks for Python (linting and mypy) and frontend, as well as the execution of unit tests in GitHub Workflow.
- **.vscode/**: Contains settings for linting and launching the webapp from VSCode.
- **backend/**: Contains server files.
- **code-env/**: Contains backend requirements.
- **resource/**: Contains the frontend.
- **tests/**: Contains unit tests.
- **Makefile**: Contains commands to run static checks, linting, and unit tests.
- **pyproject.toml**: Contains linting rules for ruff and mypy.

## Installation with uv

1. Create a virtual environment with `uv`:
    ```sh
    uv venv --python <version>
    ```
2. Activate the virtual environment:
    ```sh
    source .venv/bin/activate
    ```
3. Install the dependencies:
    ```sh
    uv pip install -r code-env/python/spec/requirements.dev.txt
    ```
4. Create .env file and add, make sure to update the values:
    ```
    LOCAL_DEV="true"
    DKU_CURRENT_PROJECT_KEY="your_project_key"
    VITE_API_PORT="5000"
    VITE_CLIENT_PORT="4200"
    VITE_USER_PROFILE="READER"
    ```
5. Create symbolic links to `dataiku` and `dataikuapi` packages from your local DSS_INSTANCE:
    ```sh
    cd your_venv/lib/<PYTHON_VERSION>/site-packages
    ln -s ~/Library/DataScienceStudio/kits/dataiku-dss-<DSS_VERSION>-osx/python/dataikuapi .
    ln -s ~/Library/DataScienceStudio/kits/dataiku-dss-<DSS_VERSION>-osx/python/dataiku .
    ```
    Make sure to replace by your PYTHON and DSS versions .

6. Create a `local_config.json` in `python-lib/backend` and replace these settings with your config: 
```json
{
    "LLMs": [
        {
            "$$hashKey": "object:287",
            "llm_id": "openai:bs-openai:gpt-4o"
        },
    ],
    "tool_agent_configurations": [
        {
            "$$hashKey": "object:4005",
            "agent_id": "AGENTCONNECT:agent:rMtLeB1n",
            "tool_agent_description": "A specialised Dataiku ARR-2025 data assistant.\nInvoke this tool whenever the user’s question requires factual, numeric, or tabular insight drawn from Dataiku’s FY-2025 subscription-contract dataset (ARR, pipeline, seats, renewal probability, churn risk, usage hours, NPS, regions, industries, segments, tiers, dates, etc.).",
            "agent_system_instructions": "Tone \u0026 formatting guidelines\nProfessional, succinct, friendly."
        }
    ],
    "orchestration_mode": "tools",
    "projects_keys": [
        "AGENTCONNECT"
    ],
    "agents_ids": [
        "AGENTCONNECT:agent:rMtLeB1n"
    ],
    "tools": [
        "yGuEuEr", "DWV1vca"
    ],
    "augmented_llms_ids": [
      "PORTAL:retrieval-augmented-llm:cba1f221"
    ],
    "augmented_llms_configurations": [
      {
        "$$hashKey": "object:465",
        "augmented_llm_id": "PORTAL:retrieval-augmented-llm:cba1f221",
        "augmented_llm_description": "Knows everything about genai trends"
      }
    ],
    "db_folder_path":"local_folder_path_to_store_db_files",
    "default_fs_connection": "filesystem_managed"
}
```
7. `cd` into `python-lib` and run the backend

```sh
    cd python-lib
    python -m backend.wsgi
```

## Local Backend Development

For local development, you need to apply the following changes:

1. Comment out  `with dataiku.WebappImpersonationContext() as context:` in
  - `python-lib/backend/utils/agents_utils.py`
  - `python-lib/backend/routes/user.py`
  - `python-lib/backend/utils/dss_utils.py`

Like so 👇

```python
# with dataiku.WebappImpersonationContext() as context:
```

2. In `python-lib/backend/utils/ws_utils.py` replace
- ~~`g.authIdentifier = None`~~
- `g.authIdentifier = "admin"`

```python
g.authIdentifier = "admin"
```

3. In `ChatInput.vue` replace
- ~~`formData.append('user_id', userId.value || '')`~~
- `formData.append('user_id', userId.value || 'admin')`

4. In `user_utils.py` comment out : 
```
# Only send callerTicket for dss >= 14.3.2 to make sure they are redacted properly
    dss_version = client.get_instance_info().raw.get("dssVersion", "0.0.0")
    if (compare_versions(dss_version, "14.3.2")) >= 0:
        cont["dkuCallerTicket"] = client.get_ticket_from_browser_headers(headers_dict=dict(request.headers))
```
### Dev Mode

This setup lets you keep local-only changes (like debug settings) without committing them by mistake.

---

### Create the patch
```bash
git diff > dev.patch
```
### Enable dev mode
```bash
make dev-on
```
### Disable dev mode
```bash
make dev-off
```
### Commit protection

If dev.patch is still applied, commits will be blocked with: ⚠️ dev.patch is applied. Run: make dev-off

dev.patch is ignored by Git.

## Database Migrations with Alembic

This project uses Alembic to manage database schema migrations.

### 1. Environment Setup

Before running any Alembic commands, you need to create a `.env` file in the root of the project. This file should contain the following:

```
DATABASE_URL=your_database_url
```

Replace `your_database_url` with the connection string for your database. For example:

```
DATABASE_URL=sqlite:///data_store.db
```
- Run
```
export PYTHONPATH=$(pwd)/python-lib
```

### 2. Generating a New Migration

When you make changes to your SQLAlchemy models, you need to generate a new migration script. This script will contain the necessary code to update the database schema.

To generate a new migration, run the following command:

```bash
.venv/bin/alembic revision --autogenerate -m "Your migration message"
```

Replace `"Your migration message"` with a short, descriptive message about the changes you made.

This will create a new file in the `alembic/versions` directory. Review and adapt as needed.

### 3. Applying a Migration

To apply the latest migration to your database, run the following command:

```bash
.venv/bin/alembic upgrade head
```

This will execute the migration scripts and update your database schema to match your models.

### 4. Managing an Existing Database

If you are integrating Alembic into a project with an existing database, you should not run `alembic upgrade head` directly. This will result in errors because Alembic will try to create tables that already exist.

Instead, you need to "stamp" the database with the latest revision. This tells Alembic that the database is already at the latest version, without running any migrations.

To stamp your database, run the following command:

```bash
PYTHONPATH=python-lib .venv/bin/alembic stamp head
```

After stamping, you can generate and apply new migrations as described in the sections above.

### 5. Production & Team Workflow

The `alembic/versions` directory is the source of truth for your database schema. To ensure consistency across all environments (dev, production, etc.), you should:

1.  **Commit Migration Scripts:** Always commit the migration scripts generated in the `alembic/versions` directory.
2.  **Run Migrations on Deploy:** Include the `alembic upgrade head` command in your deployment process. This will ensure that the production database is always up-to-date with the latest schema.
3.  **Staying in Sync:** You should run `alembic upgrade head` after pulling the latest changes to update your local databases.

## Evolution

This template will evolve over time.