Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

cleanup dependent default resolution #465

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
11 changes: 9 additions & 2 deletions .github/actions/setup-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ runs:
with:
miniforge-variant: Mambaforge
miniforge-version: latest
activate-environment: ragna-dev
activate-environment: ragna-deploy-dev

- name: Display conda info
shell: bash -el {0}
Expand Down Expand Up @@ -57,6 +57,13 @@ runs:
shell: bash -el {0}
run: playwright install

- name: Install dev dependencies
shell: bash -el {0}
run: |
pip install \
git+https://github.com/bokeh/bokeh-fastapi.git@main \
git+https://github.com/holoviz/panel@7377c9e99bef0d32cbc65e94e908e365211f4421

- name: Install ragna
shell: bash -el {0}
run: |
Expand All @@ -66,7 +73,7 @@ runs:
else
PROJECT_PATH='.'
fi
pip install --editable "${PROJECT_PATH}"
pip install --verbose --editable "${PROJECT_PATH}"

- name: Display development environment
shell: bash -el {0}
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ jobs:
matrix:
os:
- ubuntu-latest
- windows-latest
# FIXME
# Building panel from source on Windows does not work through pip
# - windows-latest
- macos-latest
python-version: ["3.9"]
include:
Expand Down
2 changes: 1 addition & 1 deletion environment-dev.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: ragna-dev
name: ragna-deploy-dev
channels:
- conda-forge
dependencies:
Expand Down
15 changes: 11 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[build-system]
requires = [
"setuptools>=45",
"setuptools_scm[toml]>=6.2",
"setuptools>=64",
"setuptools_scm[toml]>=8",
]
build-backend = "setuptools.build_meta"

Expand All @@ -23,11 +23,14 @@ requires-python = ">=3.9"
dependencies = [
"aiofiles",
"emoji",
"eval_type_backport; python_version<'3.10'",
"fastapi",
"httpx",
"importlib_metadata>=4.6; python_version<'3.10'",
"packaging",
"panel==1.4.4",
# FIXME: pin them to released versions
"bokeh-fastapi",
"panel",
"pydantic>=2",
"pydantic-core",
"pydantic-settings>=2",
Expand Down Expand Up @@ -141,6 +144,9 @@ disallow_incomplete_defs = false

[[tool.mypy.overrides]]
module = [
# FIXME: the package should be typed
"bokeh_fastapi",
"bokeh_fastapi.handler",
"docx",
"fitz",
"ijson",
Expand All @@ -149,12 +155,13 @@ module = [
"pptx",
"pyarrow",
"sentence_transformers",
"traitlets",
]
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = [
"ragna.deploy._api.orm",
"ragna.deploy._orm",
]
# Our ORM schema doesn't really work with mypy. There are some other ways to define it
# to play ball. We should do that in the future.
Expand Down
2 changes: 1 addition & 1 deletion ragna/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ragna.deploy._cli import app
from ragna._cli import app

if __name__ == "__main__":
app()
File renamed without changes.
58 changes: 22 additions & 36 deletions ragna/deploy/_cli/config.py → ragna/_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def _handle_unmet_requirements(components: Iterable[Type[Component]]) -> None:
return

rich.print(
"You have selected components, which have additional requirements that are"
"You have selected components, which have additional requirements that are "
"currently not met."
)
unmet_requirements_by_type = _split_requirements(unmet_requirements)
Expand Down Expand Up @@ -251,51 +251,37 @@ def _wizard_common() -> Config:
).unsafe_ask()
)

for sub_config, title in [(config.api, "REST API"), (config.ui, "web UI")]:
sub_config.hostname = questionary.text( # type: ignore[attr-defined]
f"What hostname do you want to bind the the Ragna {title} to?",
default=sub_config.hostname, # type: ignore[attr-defined]
qmark=QMARK,
).unsafe_ask()

sub_config.port = int( # type: ignore[attr-defined]
questionary.text(
f"What port do you want to bind the the Ragna {title} to?",
default=str(sub_config.port), # type: ignore[attr-defined]
qmark=QMARK,
).unsafe_ask()
)

config.api.database_url = questionary.text(
"What is the URL of the SQL database?",
default=Config(local_root=config.local_root).api.database_url,
config.hostname = questionary.text(
"What hostname do you want to bind the the Ragna server to?",
default=config.hostname,
qmark=QMARK,
).unsafe_ask()

config.api.url = questionary.text(
"At which URL will the Ragna REST API be served?",
default=Config(
api=dict( # type: ignore[arg-type]
hostname=config.api.hostname,
port=config.api.port,
)
).api.url,
qmark=QMARK,
).unsafe_ask()
config.port = int(
questionary.text(
"What port do you want to bind the the Ragna server to?",
default=str(config.port),
qmark=QMARK,
).unsafe_ask()
)

config.api.origins = config.ui.origins = [
config.origins = [
questionary.text(
"At which URL will the Ragna web UI be served?",
"At which URL will Ragna be served?",
default=Config(
ui=dict( # type: ignore[arg-type]
hostname=config.ui.hostname,
port=config.ui.port,
)
).api.origins[0],
hostname=config.hostname,
port=config.port,
).origins[0],
qmark=QMARK,
).unsafe_ask()
]

config.database_url = questionary.text(
"What is the URL of the SQL database?",
default=Config(local_root=config.local_root).database_url,
qmark=QMARK,
).unsafe_ask()

return config


Expand Down
122 changes: 122 additions & 0 deletions ragna/_cli/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from pathlib import Path
from typing import Annotated, Optional

import rich
import typer
import uvicorn

import ragna
from ragna.deploy._core import make_app

from .config import ConfigOption, check_config, init_config

app = typer.Typer(
name="Ragna",
invoke_without_command=True,
no_args_is_help=True,
add_completion=False,
pretty_exceptions_enable=False,
)


def version_callback(value: bool) -> None:
if value:
rich.print(f"ragna {ragna.__version__} from {ragna.__path__[0]}")
raise typer.Exit()


@app.callback()
def _main(
version: Annotated[
Optional[bool],
typer.Option(
"--version", callback=version_callback, help="Show version and exit."
),
] = None,
) -> None:
pass


@app.command(help="Start a wizard to build a Ragna configuration interactively.")
def init(
*,
output_path: Annotated[
Path,
typer.Option(
"-o",
"--output-file",
metavar="OUTPUT_PATH",
default_factory=lambda: Path.cwd() / "ragna.toml",
show_default="./ragna.toml",
help="Write configuration to <OUTPUT_PATH>.",
),
],
force: Annotated[
bool,
typer.Option(
"-f", "--force", help="Overwrite an existing file at <OUTPUT_PATH>."
),
] = False,
) -> None:
config, output_path, force = init_config(output_path=output_path, force=force)
config.to_file(output_path, force=force)


@app.command(help="Check the availability of components.")
def check(config: ConfigOption = "./ragna.toml") -> None: # type: ignore[assignment]
is_available = check_config(config)
raise typer.Exit(int(not is_available))


@app.command(help="Deploy Ragna REST API and web UI.")
def deploy(
*,
config: ConfigOption = "./ragna.toml", # type: ignore[assignment]
api: Annotated[
bool,
typer.Option(
"--api/--no-api",
help="Deploy the Ragna REST API.",
),
] = True,
ui: Annotated[
bool,
typer.Option(
help="Deploy the Ragna web UI.",
),
] = True,
ignore_unavailable_components: Annotated[
bool,
typer.Option(
help=(
"Ignore components that are not available, "
"i.e. their requirements are not met. "
)
),
] = False,
open_browser: Annotated[
Optional[bool],
typer.Option(
help="Open a browser when Ragna is deployed.",
show_default="value of ui / no-ui",
),
] = None,
) -> None:
if not (api or ui):
raise Exception

if open_browser is None:
open_browser = ui

uvicorn.run(
lambda: make_app(
config,
ui=ui,
api=api,
ignore_unavailable_components=ignore_unavailable_components,
open_browser=open_browser,
),
factory=True,
host=config.hostname,
port=config.port,
)
34 changes: 1 addition & 33 deletions ragna/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import threading
from pathlib import Path
from typing import Any, Callable, Optional, Union
from urllib.parse import SplitResult, urlsplit, urlunsplit

_LOCAL_ROOT = (
Path(os.environ.get("RAGNA_LOCAL_ROOT", "~/.cache/ragna")).expanduser().resolve()
Expand All @@ -28,7 +27,7 @@ def local_root(path: Optional[Union[str, Path]] = None) -> Path:
path: If passed, this is set as new local root directory.

Returns:
Ragnas local root directory.
Ragna's local root directory.
"""
global _LOCAL_ROOT
if path is not None:
Expand Down Expand Up @@ -59,37 +58,6 @@ def fix_module(globals: dict[str, Any]) -> None:
obj.__module__ = globals["__package__"]


def _replace_hostname(split_result: SplitResult, hostname: str) -> SplitResult:
# This is a separate function, since hostname is not an element of the SplitResult
# namedtuple, but only a property. Thus, we need to replace the netloc item, from
# which the hostname is generated.
if split_result.port is None:
netloc = hostname
else:
netloc = f"{hostname}:{split_result.port}"
return split_result._replace(netloc=netloc)


def handle_localhost_origins(origins: list[str]) -> list[str]:
# Since localhost is an alias for 127.0.0.1, we allow both so users and developers
# don't need to worry about it.
localhost_origins = {
components.hostname: components
for url in origins
if (components := urlsplit(url)).hostname in {"127.0.0.1", "localhost"}
}
if "127.0.0.1" in localhost_origins:
origins.append(
urlunsplit(_replace_hostname(localhost_origins["127.0.0.1"], "localhost"))
)
elif "localhost" in localhost_origins:
origins.append(
urlunsplit(_replace_hostname(localhost_origins["localhost"], "127.0.0.1"))
)

return origins


def timeout_after(
seconds: float = 30, *, message: str = ""
) -> Callable[[Callable], Callable]:
Expand Down
1 change: 0 additions & 1 deletion ragna/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from ._document import (
Document,
DocumentHandler,
DocumentUploadParameters,
DocxDocumentHandler,
LocalDocument,
Page,
Expand Down
Loading
Loading