This commit is contained in:
LASTA_DEV01\lasta
2026-05-13 19:57:34 +09:00
parent 2e9204243d
commit 9e88f4c7ad
4310 changed files with 48538 additions and 905279 deletions

View File

@@ -0,0 +1,13 @@
---
description: "Documentation conventions"
globs:
- "docs/**/*.md"
- "mkdocs.yaml"
alwaysApply: false
---
# Documentation Rules
- Write docs in Markdown with clear headings and code examples.
- Update `mkdocs.yaml` navigation when adding new docs.
- Keep `README.md` concise, and move long explanations to `/docs`.
- Use Python code fences (```python … ```) in examples.

View File

@@ -0,0 +1,13 @@
---
description: "Conventions for using Pydantic models and settings"
globs:
- "**/*.py"
alwaysApply: false
---
# Pydantic Rules
- Use `pydantic.BaseModel` for request/response schemas.
- For configuration, use `pydantic_settings.BaseSettings` (v2).
- Always provide default values or use `...` for required fields.
- Use model validators (`@field_validator`, `@model_validator`) instead of custom init logic.
- Keep models small and focused on one purpose.

View File

@@ -0,0 +1,12 @@
---
description: "Use `uv run` for running Python code instead of raw python3"
globs:
- "**/*.py"
alwaysApply: true
---
# Running Python code
- **Do not** run `python3 -c` directly.
- Always invoke Python code using `uv run python <script>` or `uv run <module>` when executing project scripts.
- Use the `uv` environment so that dependencies from `uv.lock` are respected.
- This applies to both interactive code execution and testing.

View File

@@ -0,0 +1,16 @@
---
description: "Follow project style guidelines for Python code"
globs:
- "**/*.py"
alwaysApply: true
---
# Python Coding Rules
- Use python 3.12 conventions: use `type | None` instead of `Optional` etc
- Use type hints everywhere (PEP 484).
- Use snake_case for functions and variables, PascalCase for classes.
- Use `pydantic.BaseModel` for structured data, not dataclasses
- Avoid unused imports.
- Use `async`/`await` where appropriate.
- Always keep functions small and composable.
- Avoid using hasattr at all costs, it's not safe

View File

@@ -0,0 +1,16 @@
---
description: "Testing guidelines"
globs:
- "test/**/*.py"
alwaysApply: false
---
# Testing Guidelines
- Use `pytest` with descriptive test function names.
- Prefer fixtures over global state.
- For async code, use `pytest-asyncio`.
- Ensure test coverage for:
- Pydantic model validation
- Env config via `BaseSettings`
- Error cases and edge cases
- Run tests with `uv run pytest test`.

View File

@@ -0,0 +1,11 @@
---
description: "Dependency management with uv"
globs:
- "pyproject.toml"
- "uv.lock"
alwaysApply: false
---
# Dependency Management
- Pin versions in `pyproject.toml` for reproducibility.
- Run `uv pip check` to validate the dependency tree.

View File

@@ -0,0 +1,31 @@
*
!ontocast/
!ontocast/**
!ontocast/**/*py
!test
!test/
!test/**
!test/**/*py
!test/**/*
!docs/
!docs/*
!docs/**
!docs/**/*.md
!docs/**/*.py
!data
!data/
!data/**
!.github/**
!.github/**/*yml
!.env.example
!pyproject.toml
!mkdocs.yaml
!*.md
!Dockerfile
!planning

View File

@@ -0,0 +1,98 @@
# ===========================================
# OntoCast Environment Configuration
# Copy this file to .env and update with your values
# ===========================================
# Domain Configuration
# Used for URI generation in the knowledge graph
CURRENT_DOMAIN=https://example.com
# Server Configuration
PORT=8999
RECURSION_LIMIT=1000
ESTIMATED_CHUNKS=30
MAX_VISITS=3
# LLM Configuration
# Choose your LLM provider and configure accordingly
# OpenAI Configuration (Default)
# Available providers: openai, ollama
LLM_PROVIDER=openai
# Available OpenAI models: gpt-4o, gpt-4o-mini etc
LLM_MODEL_NAME=gpt-4o-mini
LLM_TEMPERATURE=0.0
LLM_API_KEY=your_openai_api_key_here
# Ollama Configuration (Alternative)
# LLM_PROVIDER=ollama
# Available Ollama models: qwen2.5, qwen2.5:72b, llama3.1, llama3.1:70b
# LLM_MODEL_NAME=llama3.1
# LLM_BASE_URL=http://localhost:11434
# LLM_TEMPERATURE=0.0
# Chunking Configuration
CHUNK_BREAKPOINT_THRESHOLD_TYPE=percentile
CHUNK_BREAKPOINT_THRESHOLD_AMOUNT=95.0
CHUNK_BUFFER_SIZE=5
CHUNK_MIN_SIZE=2000
CHUNK_MAX_SIZE=20000
# Triple Store Configuration
# Fuseki is preferred over Neo4j when both are configured
# If no triple store is configured, OntoCast will use filesystem storage
# Fuseki Configuration (Recommended)
FUSEKI_URI=http://localhost:3030/test
FUSEKI_AUTH=admin/admin
FUSEKI_DATASET=dataset_name
# FUSEKI_ONTOLOGIES_DATASET=ontologies # Defaults to "ontologies" from constants
# Neo4j Configuration (Alternative)
NEO4J_URI=bolt://localhost:7687
NEO4J_AUTH=neo4j/test
NEO4J_PORT=7476
NEO4J_BOLT_PORT=7689
# Path Configuration
ONTOCAST_WORKING_DIRECTORY=/path/to/working/directory
ONTOCAST_ONTOLOGY_DIRECTORY=/path/to/ontology/files
ONTOCAST_CACHE_DIR=/path/to/cache/directory
# EmbeddingBasedAggregator controls
AGG_EMBEDDING_MODEL=paraphrase-multilingual-MiniLM-L12-v2
AGG_SIMILARITY_THRESHOLD=0.80
# Optional: Web grounding for ontology render/critic (DuckDuckGo)
WEB_SEARCH_ENABLED=false
WEB_SEARCH_PROVIDER=duckduckgo
WEB_SEARCH_TOP_K=3
WEB_SEARCH_TIMEOUT_SECONDS=8.0
WEB_SEARCH_MAX_SNIPPET_CHARS=400
WEB_SEARCH_MAX_TOTAL_CHARS=1800
WEB_SEARCH_ONTOLOGY_RENDER_ENABLED=true
WEB_SEARCH_ONTOLOGY_CRITIC_ENABLED=true
WEB_SEARCH_FACTS_RENDER_ENABLED=false
WEB_SEARCH_FACTS_CRITIC_ENABLED=false
WEB_SEARCH_PLANNER_ENABLED=true
WEB_SEARCH_PLANNER_MAX_QUERIES=3
WEB_SEARCH_PLANNER_MIN_QUERY_CHARS=12
WEB_SEARCH_PLANNER_MIN_CONFIDENCE=0.35
WEB_SEARCH_REUSE_EVIDENCE_ACROSS_ATTEMPT=true
WEB_SEARCH_MIN_SNIPPET_CHARS=40
WEB_SEARCH_ALLOWED_DOMAINS=
WEB_SEARCH_BLOCKED_DOMAINS=
WEB_SEARCH_REGION=wt-wt
WEB_SEARCH_SAFESEARCH=moderate
# Additional Configuration
# Set to true to clean triple store on startup
CLEAN=false
# Set to true to skip ontology critique
RENDER_MODE=ontology
#RENDER_MODE=facts
#RENDER_MODE=ontology_and_facts
# Maximum number of triples allowed in ontology graph (set to empty for unlimited)
ONTOLOGY_MAX_TRIPLES=10000
# Set logging level (debug, info, warning, error)
LOGGING_LEVEL=info

View File

@@ -0,0 +1,54 @@
name: Build GitHub Pages
on:
push:
branches:
- main
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'ontocast/**' # Check this path
workflow_dispatch:
permissions:
contents: write
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.5"
- name: Install dependencies
run: |
uv sync --group docs --no-group dev
- name: Build site
run: |
uv run mkdocs build --site-dir _site
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: _site
deploy:
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View File

@@ -0,0 +1,20 @@
name: pre-commit
on:
pull_request:
push:
branches: [main]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.28"
- name: Install dependencies (including dev)
run: uv sync --group dev
- name: Run pre-commit
run: uv run pre-commit run --all-files

View File

@@ -0,0 +1,35 @@
name: Upload Python Package to PyPI
on:
release:
types: [published]
permissions:
contents: read
jobs:
pypi-publish:
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/ontocast
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.9.5"
- name: Build and publish
run: |
uv build
uv publish
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}

View File

@@ -0,0 +1,182 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
.vscode/
.idea/
.env*
!.env.example

View File

@@ -0,0 +1,46 @@
fail_fast: false
repos:
# Python linting and formatting
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.10
hooks:
- id: ruff-check
args: [--fix, --ignore, E722, --ignore, W293, --ignore, E501]
- id: ruff-format
# Type checking
- repo: local
hooks:
- id: ty
name: ty check
entry: bash -c 'ty check ontocast test'
language: system
pass_filenames: false
always_run: true
# YAML formatting
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.15.0
hooks:
- id: pretty-format-yaml
args: [--autofix, --indent, '4', --preserve-quotes]
# JSON formatting
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: pretty-format-json
args: [--autofix, --indent, '4', --no-sort-keys]
# TOML formatting and sorting
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.15.0
hooks:
- id: pretty-format-toml
args: [--autofix]
- repo: https://github.com/pappasam/toml-sort
rev: v0.24.3
hooks:
- id: toml-sort
args: [-ia]

View File

@@ -0,0 +1 @@
3.12

View File

@@ -0,0 +1,178 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.0] - 2026-03-10
### Added
- `updated_at` timestamp field in Ontology properties for tracking last update time.
- Automatic semantic versioning with intelligent MAJOR/MINOR/PATCH increment analysis.
- Version analysis based on ontology changes (classes, properties, and instances).
- Hash-based versioning with parent hashes for git-style lineage tracking.
- `mark_as_updated()` in Ontology for version/timestamp management.
- `sync_properties_to_graph()` to persist `version` and `updated_at` in RDF.
- `versioned_iri` support for storing multiple ontology versions in triple stores.
- URL encoding for versioned IRIs in Fuseki to preserve `#` in named graph URIs.
- Multi-version ontology storage in Fuseki using separate named graphs.
- Automatic ontology synchronization from filesystem to triple store during initialization.
- `render_mode` processing options: `ontology`, `facts`, `ontology_and_facts`.
- Dedicated `serialize` workflow node; separated aggregation and serialization stages.
- API support for `render_mode` as a query parameter.
- **GraphUpdate** system with structured SPARQL insert/delete operations.
- `GraphUpdate`/`TripleOp` models for incremental graph modifications.
- `render_ontology_update()` and `render_facts_update()` GraphUpdate-based rendering.
- Automatic SPARQL generation from GraphUpdate operations.
- Budget tracking integrated in `AgentState`, including ontology/facts generation metrics.
- End-of-run budget summary reporting.
- Dependency-injected budget tracking for LLM calls.
- Shared caching architecture with a single `Cacher` instance and `ToolCacher` wrapper.
- `ONTOCAST_CACHE_DIR` environment variable for cache location.
- `serialize()` as a primary triple-manager interface for `Ontology` and `RDFGraph` objects.
- `ONTOLOGY_MAX_TRIPLES` guardrail to prevent unbounded ontology growth.
- Limit checks in `render_updated_graph()` and `sublimate_ontology()`.
- Parallel unit/chunk processing with configurable worker concurrency and retry behavior.
- More robust entity/property disambiguation across units/chunks during aggregation.
- Optional ontology consolidation switch via `ENABLE_ONTOLOGY_CONSOLIDATION`.
- Aggregation configuration via `AGG_EMBEDDING_MODEL` and `AGG_SIMILARITY_THRESHOLD`.
- Web grounding configuration surface (`WEB_SEARCH_*`) with planner, retry, evidence-budget, and domain filtering controls.
- `FUSEKI_ONTOLOGIES_DATASET` for separate ontology dataset configuration.
### Changed
- **BREAKING**: `serialize()` is now the primary interface for storing data in triple stores.
- **BREAKING**: `serialize()` now accepts `Ontology | RDFGraph` objects instead of raw `Graph` objects.
- **BREAKING**: `serialize_graph()` signature now uses `**kwargs` for backend-specific parameters.
- All triple store managers now implement both `serialize()` and `serialize_graph()`.
- **BREAKING**: Environment variables now use `ONTOCAST_` prefix:
- `WORKING_DIRECTORY``ONTOCAST_WORKING_DIRECTORY`
- `ONTOLOGY_DIRECTORY``ONTOCAST_ONTOLOGY_DIRECTORY`
- `LLM_CACHE_DIR``ONTOCAST_CACHE_DIR`
- **BREAKING**: Ontology and facts rendering now use GraphUpdate/SPARQL operations instead of full TTL generation.
- LLM output now uses structured `GraphUpdate` + `TripleOp`, reducing token usage.
- Ontology version increments now derive from detected ontology diffs.
- Version updates now happen once at end of processing (`serialize`).
- LLM tool budget tracking refactored to dependency injection.
- Global `LLMBudgetTracker` replaced by AgentState-contained tracker.
- Agent functions updated to use injection-based budget plumbing.
- Server recursion control renamed to `BASE_RECURSION_LIMIT` (instead of `RECURSION_LIMIT`).
- `MAX_VISITS` remains supported as alias for `max_visits_per_node`.
- Default `ONTOLOGY_MAX_TRIPLES` increased to `50000`.
- Docs updated for new configuration sections and defaults (`Server`, `Aggregation`, and `Web Search`).
### Removed
- Global budget tracker state management.
- Manual budget tracker update calls inside agent functions.
- `set_budget_tracker()` and `get_budget_tracker()` functions.
## [0.1.7] - 2025-10
### Added
- Automatic LLM response caching for improved performance and cost reduction
- Platform-aware default cache directory selection
- Transparent caching with no configuration required
- Environment variable `SKIP_ONTOLOGY_DEVELOPMENT` to skip ontology critique step
- Environment variable `LLM_API_KEY` for LLM authentication (replaces `OPENAI_API_KEY`)
- Environment variable `MAX_VISITS` for controlling workflow behavior
- Environment variable `WORKING_DIRECTORY` for specifying working directory
- Environment variable `ONTOLOGY_DIRECTORY` for specifying ontology files
- Hierarchical configuration system with environment variable support
- Support for `.env` file configuration
- Python 3.12 type hint support (`str | None` syntax)
- `pathlib.Path` support for directory configurations
- Improved RDF graph operations with proper prefix binding
### Changed
- `OPENAI_API_KEY` environment variable renamed to `LLM_API_KEY`
- Configuration system refactored to use dependency injection
- `ToolBox` now accepts configuration objects directly
- `LLMTool` now accepts configuration objects directly
- Type annotations updated to Python 3.12 standards
- Path handling updated to use `pathlib.Path` objects
- Triple store configuration moved to environment variables
### Fixed
- RDF graph prefix binding issues
- Configuration validation errors
- Triple store initialization errors
- API key handling in LLM configuration
- Type annotation compatibility issues
### Removed
- Global configuration variable
- Support for `OPENAI_API_KEY` environment variable
- Individual parameter passing in tool initialization
### Security
- API keys now handled with secure string types
- Configuration validation prevents data exposure
## [0.1.5] - 2025-01-XX
### Added
- Automatic LLM response caching for improved performance and cost reduction
- Platform-aware default cache directory selection (avoids /tmp)
- Transparent caching with no configuration required
- Version bump to 0.1.5
- Various stability improvements
---
## Migration Guide
### Environment Variables
```bash
# Old
OPENAI_API_KEY=your_key_here
# New
LLM_API_KEY=your_key_here
```
### Configuration Usage
```python
# Old way (no longer supported)
from ontocast.config import config
llm_provider = config.llm_config.provider
# New way
from ontocast.config import Config
config = Config()
llm_provider = config.tool_config.llm_config.provider
```
### ToolBox Initialization
```python
# Old way (no longer supported)
tools = ToolBox(
llm_provider="openai",
model_name="gpt-4",
# ... many individual parameters
)
# New way
tools = ToolBox(config)
```
### CLI Parameters
### LLM Caching
```python
# Caching is now automatic - no configuration needed
```
```bash
# Skip ontology critique step
ontocast --skip-ontology-critique
# Or set environment variable
export SKIP_ONTOLOGY_DEVELOPMENT=true
ontocast --env-path .env
```

View File

@@ -0,0 +1,14 @@
abstract: OntoCast is a framework for extracting semantic triples (creating a Knowledge Graph) from documents using an agentic, ontology-driven approach. It combines ontology management, natural language processing, and knowledge graph serialization to turn unstructured text into structured, queryable data.
authors:
- affiliation: GrowGraph
family-names: Belikov
given-names: Alexander
orcid: 0000-0002-5649-0913
cff-version: 0.2.3
date-released: '2025-12-03'
doi: 10.5281/zenodo.17796467
license: []
license-url: https://github.com/growgraph/ontocast/blob/main/LICENSE
message: If you use this software, please cite it using the metadata from this file.
title: ontocast
type: software

View File

@@ -0,0 +1,59 @@
# ─── builder stage ───────────────────────────────────────────────────────────
FROM python:3.12-slim-bullseye AS builder
RUN apt update -y \
&& apt install -y curl git \
&& curl -LsSf https://astral.sh/uv/0.9.5/install.sh | sh \
&& rm -rf /var/lib/apt/lists/*
ENV PATH="${PATH}:/root/.local/bin"
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN touch README.md
# uv creates a venv and installs deps
RUN --mount=type=ssh uv sync --no-group dev --no-group docs -v
COPY ontocast ./ontocast
COPY README.md ./
# ─── runtime stage ────────────────────────────────────────────────────────────
FROM python:3.12-slim-bullseye AS runtime
LABEL org.opencontainers.image.title="ontocast-mcp-server" \
org.opencontainers.image.version="0.1.1" \
org.opencontainers.image.description="Ontologyassisted semantic triple extractor (MCPcompatible)"
# Install curl for healthcheck
RUN apt update -y \
&& apt install -y curl \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r ontocast && useradd -r -g ontocast ontocast
USER ontocast
WORKDIR /app
COPY --from=builder /app /app
# ─── Volume Mounting Notes ──────────────────────────────────────────────────────
# Paths are configured via .env file (ONTOCAST_WORKING_DIRECTORY,
# ONTOCAST_ONTOLOGY_DIRECTORY, ONTOCAST_CACHE_DIR) and should be mounted
# as volumes in docker-compose to persist data and allow host access.
#
# Example docker-compose volumes:
# volumes:
# - ./data/working:/path/to/working
# - ./data/ontologies:/path/to/ontologies
# - ./data/cache:/path/to/cache
#
# Ensure the paths in .env match the container-side mount paths.
# Do NOT hardcode paths in Dockerfile - they must come from .env configuration.
# Expose & healthcheck
EXPOSE 8999
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:8999/health || exit 1
ENTRYPOINT ["uv", "run", "ontocast"]

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,391 @@
# OntoCast <img src="https://raw.githubusercontent.com/growgraph/ontocast/refs/heads/main/docs/assets/favicon.ico" alt="Agentic Ontology Triplecast logo" style="height: 32px; width:32px;"/>
### Agentic ontology-assisted framework for semantic triple extraction
![Python](https://img.shields.io/badge/python-3.12-blue.svg)
[![PyPI version](https://badge.fury.io/py/ontocast.svg)](https://badge.fury.io/py/ontocast)
[![PyPI Downloads](https://static.pepy.tech/badge/ontocast)](https://pepy.tech/projects/ontocast)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![pre-commit](https://github.com/growgraph/ontocast/actions/workflows/pre-commit.yml/badge.svg)](https://github.com/growgraph/ontocast/actions/workflows/pre-commit.yml)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.17796467.svg)](https://doi.org/10.5281/zenodo.17796467)
---
## Overview
OntoCast is a framework for extracting semantic triples (creating a Knowledge Graph) from documents using an agentic, ontology-driven approach. It combines ontology management, natural language processing, and knowledge graph serialization to turn unstructured text into structured, queryable data.
---
## Key Features
- **Ontology-Guided Extraction**: Ensures semantic consistency and co-evolves ontologies
- **Entity Disambiguation**: Resolves references across document chunks
- **Multi-Format Support**: Handles text, JSON, PDF, and Markdown
- **Semantic Chunking**: Splits text based on semantic similarity
- **MCP Compatibility**: Implements Model Control Protocol endpoints
- **RDF Output**: Produces standardized RDF/Turtle
- **Triple Store Integration**: Supports Neo4j (n10s) and Apache Fuseki
- **Hierarchical Configuration**: Type-safe configuration system with environment variable support
- **CLI Parameters**: Flexible command-line interface with `--skip-ontology-critique` option
- **Automatic LLM Caching**: Built-in response caching for improved performance and cost reduction
- **GraphUpdate Operations**: Token-efficient SPARQL-based updates instead of full graph regeneration
- **Budget Tracking**: Comprehensive tracking of LLM usage and triple generation metrics
- **Ontology Versioning**: Automatic semantic versioning with hash-based lineage tracking
---
## Applications
OntoCast can be used for:
- **Knowledge Graph Construction**: Build domain-specific or general-purpose knowledge graphs from documents
- **Semantic Search**: Power search and retrieval with structured triples
- **GraphRAG**: Enable retrieval-augmented generation over knowledge graphs (e.g., with LLMs)
- **Ontology Management**: Automate ontology creation, validation, and refinement
- **Data Integration**: Unify data from diverse sources into a semantic graph
---
## Installation
```sh
uv add ontocast[doc-processing]
# or
pip install ontocast
```
### Optional features: document processing (PDFs, PPT, OCR, semantic chunking):
```sh
uv add "ontocast[doc-processing]"
# or
pip install "ontocast[doc-processing]"
```
---
## Quick Start
### 1. Configuration
Create a `.env` file with your configuration:
```bash
# LLM Configuration
LLM_PROVIDER=openai
LLM_API_KEY=your-api-key-here
LLM_MODEL_NAME=gpt-4o-mini
LLM_TEMPERATURE=0.1
# Server Configuration
PORT=8999
MAX_VISITS=3
RECURSION_LIMIT=1000
ESTIMATED_CHUNKS=30
ONTOLOGY_MAX_TRIPLES=10000
# Path Configuration
ONTOCAST_WORKING_DIRECTORY=/path/to/working
ONTOCAST_ONTOLOGY_DIRECTORY=/path/to/ontologies
ONTOCAST_CACHE_DIR=/path/to/cache
# Optional: Triple Store Configuration
FUSEKI_URI=http://localhost:3032/test
FUSEKI_AUTH=admin:password
FUSEKI_DATASET=ontocast
# Optional: Skip ontology critique
SKIP_ONTOLOGY_DEVELOPMENT=false
# Optional: Maximum triples allowed in ontology graph (set empty for unlimited)
ONTOLOGY_MAX_TRIPLES=10000
# Optional: Web search grounding (search-later mode)
# Node execution starts without search; search runs only when node output requests it.
WEB_SEARCH_ENABLED=false
WEB_SEARCH_PROVIDER=duckduckgo
WEB_SEARCH_TOP_K=3
WEB_SEARCH_TIMEOUT_SECONDS=8.0
WEB_SEARCH_MAX_SNIPPET_CHARS=400
WEB_SEARCH_MAX_TOTAL_CHARS=1800
WEB_SEARCH_ONTOLOGY_RENDER_ENABLED=true
WEB_SEARCH_ONTOLOGY_CRITIC_ENABLED=true
WEB_SEARCH_FACTS_RENDER_ENABLED=false
WEB_SEARCH_FACTS_CRITIC_ENABLED=false
WEB_SEARCH_PLANNER_ENABLED=true
WEB_SEARCH_PLANNER_MAX_QUERIES=3
WEB_SEARCH_PLANNER_MIN_QUERY_CHARS=12
WEB_SEARCH_PLANNER_MIN_CONFIDENCE=0.35
WEB_SEARCH_REUSE_EVIDENCE_ACROSS_ATTEMPT=true
WEB_SEARCH_MIN_SNIPPET_CHARS=40
WEB_SEARCH_ALLOWED_DOMAINS=
WEB_SEARCH_BLOCKED_DOMAINS=
```
### 2. Start Server
```bash
ontocast \
--env-path .env \
--working-directory /path/to/working \
--ontology-directory /path/to/ontologies
```
### 3. Process Documents
```bash
curl -X POST http://localhost:8999/process -F "file=@document.pdf"
```
### 4. API Endpoints
The OntoCast server provides the following endpoints:
- **POST /process**: Process documents and extract semantic triples
```bash
curl -X POST http://localhost:8999/process -F "file=@document.pdf"
```
- **POST /flush**: Flush/clean triple store data
```bash
# Clean all datasets (Fuseki) or entire database (Neo4j)
curl -X POST http://localhost:8999/flush
# Clean specific Fuseki dataset
curl -X POST "http://localhost:8999/flush?dataset=my_dataset"
```
**Note:** For Fuseki, you can specify a `dataset` query parameter to clean a specific dataset. If omitted, all datasets are cleaned. For Neo4j, the `dataset` parameter is ignored and all data is deleted.
- **GET /health**: Health check endpoint
- **GET /info**: Service information endpoint
---
## LLM Caching
OntoCast includes automatic LLM response caching to improve performance and reduce API costs. Caching is enabled by default and requires no configuration.
### Cache Locations
- **Tests**: `.test_cache/llm/` in the current working directory
- **Windows**: `%USERPROFILE%\AppData\Local\ontocast\llm\`
- **Unix/Linux**: `~/.cache/ontocast/llm/` (or `$XDG_CACHE_HOME/ontocast/llm/`)
### Benefits
- **Faster Execution**: Repeated queries return cached responses instantly
- **Cost Reduction**: Identical requests don't hit the LLM API
- **Offline Capability**: Tests can run without API access if responses are cached
- **Transparent**: No configuration required - works automatically
### Custom Cache Directory
If you need to specify a custom cache directory:
```python
from pathlib import Path
from ontocast.tool.llm import LLMTool
# Cache directory is managed automatically by Cacher
llm_tool = LLMTool.create(
config=llm_config
)
```
## Configuration System
OntoCast uses a hierarchical configuration system built on Pydantic BaseSettings:
### Environment Variables
| Variable | Description | Default | Required |
|----------|-------------|---------|----------|
| `LLM_API_KEY` | API key for LLM provider | - | Yes |
| `LLM_PROVIDER` | LLM provider (openai, ollama) | openai | No |
| `LLM_MODEL_NAME` | Model name | gpt-4o-mini | No |
| `LLM_TEMPERATURE` | Temperature setting | 0.1 | No |
| `ONTOCAST_WORKING_DIRECTORY` | Working directory path | - | Yes |
| `ONTOCAST_ONTOLOGY_DIRECTORY` | Ontology files directory | - | No |
| `PORT` | Server port | 8999 | No |
| `MAX_VISITS` | Maximum visits per node | 3 | No |
| `SKIP_ONTOLOGY_DEVELOPMENT` | Skip ontology critique | false | No |
| `ONTOLOGY_MAX_TRIPLES` | Maximum triples allowed in ontology graph | 10000 | No |
| `SKIP_FACTS_RENDERING` | Skip facts rendering and go straight to aggregation | false | No |
| `ONTOCAST_CACHE_DIR` | Custom cache directory for LLM responses | Platform default | No |
| `WEB_SEARCH_ENABLED` | Enable optional web grounding (search runs only on node request) | false | No |
| `WEB_SEARCH_PROVIDER` | Web search provider | duckduckgo | No |
| `WEB_SEARCH_TOP_K` | Number of search results used per call | 3 | No |
| `WEB_SEARCH_ONTOLOGY_RENDER_ENABLED` | Allow search-eligible ontology render retries | true | No |
| `WEB_SEARCH_ONTOLOGY_CRITIC_ENABLED` | Allow search-eligible ontology critic retries | true | No |
| `WEB_SEARCH_FACTS_RENDER_ENABLED` | Allow search-eligible facts render retries | false | No |
| `WEB_SEARCH_FACTS_CRITIC_ENABLED` | Allow search-eligible facts critic retries | false | No |
| `WEB_SEARCH_PLANNER_ENABLED` | Use LLM planner for query decisioning | true | No |
| `WEB_SEARCH_PLANNER_MAX_QUERIES` | Maximum planned focused queries per node | 3 | No |
| `WEB_SEARCH_PLANNER_MIN_QUERY_CHARS` | Guardrail minimum query length | 12 | No |
| `WEB_SEARCH_PLANNER_MIN_CONFIDENCE` | Guardrail minimum planner confidence | 0.35 | No |
| `WEB_SEARCH_ALLOWED_DOMAINS` | Optional comma-separated allowlist domains | empty | No |
| `WEB_SEARCH_BLOCKED_DOMAINS` | Optional comma-separated blocklist domains | empty | No |
### Triple Store Configuration
```bash
# Fuseki (Preferred)
FUSEKI_URI=http://localhost:3032/test
FUSEKI_AUTH=admin:password
FUSEKI_DATASET=dataset_name
# Neo4j (Alternative)
NEO4J_URI=bolt://localhost:7689
NEO4J_AUTH=neo4j:password
```
### CLI Parameters
```bash
# Skip ontology critique step
ontocast --skip-ontology-critique
# Process only first N chunks (for testing)
ontocast --head-chunks 5
```
---
## Triple Store Setup
OntoCast supports multiple triple store backends with automatic fallback:
1. **Apache Fuseki** (Recommended) - Native RDF with SPARQL support
2. **Neo4j with n10s** - Graph database with RDF capabilities
3. **Filesystem** (Fallback) - Local file-based storage
When multiple triple stores are configured, **Fuseki is preferred over Neo4j**.
### Quick Setup with Docker
**Fuseki:**
```bash
cd docker/fuseki
cp .env.example .env
# Edit .env with your values
docker compose --env-file .env fuseki up -d
```
**Neo4j:**
```bash
cd docker/neo4j
cp .env.example .env
# Edit .env with your values
docker compose --env-file .env neo4j up -d
```
See [Triple Store Setup](docs/user_guide/triple_stores.md) for detailed instructions.
---
## Documentation
- [Quick Start Guide](docs/getting_started/quickstart.md) - Get started quickly
- [Configuration System](docs/user_guide/configuration.md) - Detailed configuration guide
- [Triple Store Setup](docs/user_guide/triple_stores.md) - Triple store configuration
- [User Guide](docs/user_guide/concepts.md) - Core concepts and workflow
- [API Reference](docs/reference/onto.md) - Detailed API documentation
---
## Recent Changes
### Ontology Management Improvements
- **Automatic Versioning**: Semantic version increment based on change analysis (MAJOR/MINOR/PATCH)
- **Hash-Based Lineage**: Git-style versioning with parent hashes for tracking ontology evolution
- **Multiple Version Storage**: Versions stored as separate named graphs in Fuseki triple stores
- **Timestamp Tracking**: `updated_at` field tracks when ontology was last modified
- **Smart Version Analysis**: Analyzes ontology changes (classes, properties, instances) to determine appropriate version bump
### GraphUpdate System
- **Token Efficiency**: LLM outputs structured SPARQL operations (insert/delete) instead of full TTL graphs
- **Incremental Updates**: Only changes are generated, dramatically reducing token usage
- **Structured Operations**: TripleOp operations with explicit prefix declarations for precise updates
- **SPARQL Generation**: Automatic conversion of operations to executable SPARQL queries
### Budget Tracking
- **LLM Statistics**: Tracks API calls, characters sent/received for cost monitoring
- **Triple Metrics**: Tracks ontology and facts triples generated per operation
- **Summary Reports**: Budget summaries logged at end of processing
- **Integrated Tracking**: Budget tracker integrated into AgentState for clean dependency injection
### Configuration System Overhaul
- **Hierarchical Configuration**: New `ToolConfig` and `ServerConfig` structure
- **Environment Variables**: Support for `.env` files and environment variables
- **Type Safety**: Full type safety with Python 3.12 union syntax
- **API Key**: Changed from `OPENAI_API_KEY` to `LLM_API_KEY` for consistency
- **Dependency Injection**: Removed global variables, implemented proper DI
### Enhanced Features
- **CLI Parameters**: New `--skip-ontology-critique` and `--skip-facts-rendering` parameters
- **RDFGraph Operations**: Improved `__iadd__` method with proper prefix binding
- **Triple Store Management**: Better separation between filesystem and external stores
- **Serialization Interface**: Unified `serialize()` method for storing Ontology and RDFGraph objects
- **Error Handling**: Improved error handling and validation
See [CHANGELOG.md](CHANGELOG.md) for complete details.
---
## Examples
### Basic Usage
```python
from ontocast.config import Config
from ontocast.toolbox import ToolBox
# Load configuration
config = Config()
# Initialize tools
tools = ToolBox(config)
# Process documents
# ... (use tools for processing)
```
### Server Usage
```bash
# Start server with custom configuration
ontocast \
--env-path .env \
--working-directory /data/working \
--ontology-directory /data/ontologies \
--skip-ontology-critique \
--head-chunks 10
```
---
## Contributing
We welcome contributions! Please see our [Contributing Guide](docs/contributing.md) for details.
---
## License
This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
---
## Support
- **Documentation**: [docs/](docs/)
- **Issues**: [GitHub Issues](https://github.com/growgraph/ontocast/issues)
- **Discussions**: [GitHub Discussions](https://github.com/growgraph/ontocast/discussions)

View File

@@ -0,0 +1,43 @@
# OntoCast Vendored Copy — 수정 내역
본 디렉터리는 OntoCast의 vendored copy다.
- **원본 저장소**: https://github.com/growgraph/ontocast
- **원본 라이선스**: Apache License 2.0 (`LICENSE` 파일 참조, 변경 금지)
- **vendored 시점 버전**: 0.3.0
- **vendored 시점**: 2026-05-13
## Apache 2.0 의무사항
본 vendored copy 내의 파일을 수정할 경우 Apache License 2.0 §4(b) 요구사항에 따라:
1. 수정된 파일 상단에 `# MODIFIED YYYY-MM-DD: <한 줄 설명>` 주석을 추가한다
2. 본 문서(`VENDORED_MODIFICATIONS.md`)에 수정 내역을 항목으로 기록한다
3. 원본 `LICENSE` 파일은 절대 삭제하거나 변경하지 않는다
## 수정 내역 (시간순)
### 2026-05-13 — vendored copy 생성
- 원본 https://github.com/growgraph/ontocast (v0.3.0)을 `vendored/ontocast/`로 복사
- 아직 어떠한 파일도 수정하지 않은 상태
### 2026-05-13 — Phase 0.3: convert_document.py 다중 파일 처리 확장
- **파일**: `ontocast/agent/convert_document.py`
- **문제**: 루프는 모든 `state.files`를 순회하지만 `state.set_text(result["text"])`가 매 반복에서 덮어써 마지막 파일의 텍스트만 살아남았다(`"NB: processing only one file"` 주석으로 의도가 명시되어 있던 부분).
- **수정**: 텍스트를 `texts: list[str]`에 누적하고 파일이 2개 이상이면 `=== File: <name> ===` 경계 마커를 사이에 삽입해 단일 corpus로 합친다. 단일 파일 입력은 마커 없이 통과하여 기존 단일 파일 동작과 byte-identical. JSON envelope의 `ontology_user_instruction` / `facts_user_instruction` / `url`은 first-wins (한 corpus는 하나의 instruction이라는 모델). 첫 unsupported 확장자에서 fail-fast하는 기존 동작 유지.
- **회귀 테스트**: `tests/unit/test_convert_document.py` (7 케이스 — 단일 PDF / 단일 JSON / 다중 PDF / 다중 JSON first-wins / 미지원 확장자 / 빈 입력 / 혼합 PDF+JSON)
- **근거**: docs/통합설계서.md §5 Phase 0, OntoCast 분석 §13.1 / §21.1 (#4)
### 2026-05-13 — Phase 0.2: select_ontology.py None-index 버그 수정
- **파일**: `ontocast/agent/select_ontology.py`
- **문제**: dynamic Pydantic 모델은 `answer_index ∈ [1, num_ontologies + 1]`을 강제하지만 코드는 `answer_index == 0`을 "None"으로 처리. 0은 Pydantic 검증을 통과할 수 없어 dead code였으며, LLM이 None을 선택할 때마다(`num_ontologies + 1` 반환) defensive branch로 빠져 WARNING 로그가 찍혔다.
- **수정**: `none_index = num_ontologies + 1` 분기 추가, `answer_index == 0` 분기 제거. 파일 상단에 `MODIFIED 2026-05-13` 주석 추가.
- **회귀 테스트**: `tests/unit/test_select_ontology.py` (4 케이스 — 빈 ontology / 정상 선택 / None 선택 / 범위 밖)
- **근거**: docs/통합설계서.md §5 Phase 0, OntoCast 분석 §13.1
## 우리 프로젝트에서의 사용 방식
- `vendored/ontocast/ontocast/` 패키지는 우리 코드(`platform/`)에서 import하여 사용한다
- OntoCast의 코어 모듈(`stategraph/`, `agent/`, `onto/`, `tool/`)은 **수정 최소화**
- API 레이어(`vendored/ontocast/ontocast/cli/serve.py` Robyn)는 사용하지 않고 우리가 FastAPI로 재작성한다
- OntoCast 자체의 `pyproject.toml`은 참고용이며, 의존성은 프로젝트 루트의 `pyproject.toml`이 우선

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
{
"chunks": [
"Intern: John A. Peterson Advisor: Dr. Michael Andersson\nInstitution: Department of Physics, University of Copenhagen\n\nAbstract\n\nLow-temperature physics, or cryogenics, is a field of physics that explores the behavior of materials at temperatures approaching absolute zero. This report presents an internship study focusing on the experimental investigation of superconductivity and quantum fluids at cryogenic temperatures. Using liquid helium-based cooling techniques, we examined the superconducting transition temperature of niobium samples and performed measurements on superfluid helium. Our study provides insights into the fundamental properties of quantum materials at ultra-low temperatures and highlights challenges in experimental cryogenics."
]
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"text": "Intern: John A. Peterson Advisor: Dr. Michael Andersson\nInstitution: Department of Physics, University of Copenhagen\n\nAbstract\n\nLow-temperature physics, or cryogenics, is a field of physics that explores the behavior of materials at temperatures approaching absolute zero. This report presents an internship study focusing on the experimental investigation of superconductivity and quantum fluids at cryogenic temperatures. Using liquid helium-based cooling techniques, we examined the superconducting transition temperature of niobium samples and performed measurements on superfluid helium. Our study provides insights into the fundamental properties of quantum materials at ultra-low temperatures and highlights challenges in experimental cryogenics."
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,66 @@
# OntoCast Demo
This demo shows how OntoCast can extract a knowledge graph from a document and visualize it as a graph.
---
## What You'll See
- Turn a PDF or text document into a knowledge graph (RDF triples)
- Visualize the extracted ontology and facts using free online tools
---
## Demo Steps
### 1. Start the OntoCast Server
Make sure you have OntoCast installed and a triple store (optional) running. Then start the server:
```bash
uv run serve --ontology-directory ../data/ontologies --working-directory ../data
```
### 2. Process a Sample Document
You can use the provided `sample.pdf` or `sample.txt` in this directory.
**For PDF:**
```bash
curl -X POST http://localhost:8999/process -F "file=@demo/sample.pdf"
```
**For plain text:**
```bash
curl -X POST http://localhost:8999/process -H "Content-Type: application/json" -d '{"text": "Your document text here"}'
```
The response will include extracted facts and ontology in Turtle format.
---
### 3. Visualize the Output
1. Use neo4j built-in graph navigator
2. Copy the Turtle output from the `facts` or `ontology` field in the response.
**Example Screenshot:**
![Demo Graph Screenshot](figs/thames-water.png)
---
## Files in This Directory
- `sample.pdf` Example document for demo
- `sample.txt` Example plain text for demo
- (Optional) `sample.ttl` Example Turtle output
---
## Tips
- You can use your own documents by changing the file path in the `curl` command.
- For more details, see the [main README](../README.md) or the [Triple Store Setup Guide](../docs/user_guide/triple_stores.md).
---
## Need Help?
Open an issue or check the [OntoCast documentation](https://growgraph.github.io/ontocast).

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,173 @@
# Triple Store Configuration
OntoCast supports multiple triple store backends for storing and managing RDF data. This guide covers the setup and configuration of supported triple stores.
---
## Overview
OntoCast supports the following triple store backends:
1. **Apache Fuseki** (Recommended) - Native RDF triple store with SPARQL support
2. **Neo4j with n10s plugin** - Graph database with RDF capabilities
3. **Filesystem** - Local file-based storage (fallback)
When multiple triple stores are configured, OntoCast uses the following priority order:
1. Fuseki (if `FUSEKI_URI` and `FUSEKI_AUTH` are set)
2. Neo4j (if `NEO4J_URI` and `NEO4J_AUTH` are set)
3. Filesystem (default fallback)
---
## Environment Variables
Configure your triple store connection using environment variables in your main `.env` file:
```bash
# Fuseki Configuration (Preferred)
FUSEKI_URI=http://localhost:3032/test
FUSEKI_AUTH=admin/abc123-qwe
# Neo4j Configuration (Alternative)
NEO4J_URI=bolt://localhost:7689
NEO4J_AUTH=neo4j/test!passfortesting
```
---
## Apache Fuseki Setup
Sample configurations are provided here: [ontocast/docker](https://github.com/growgraph/ontocast/tree/main/docker).
**1. Prepare the environment file:**
```bash
cd docker/fuseki
cp .env.example .env
# Edit with your values
```
**Example `docker/fuseki/.env.example`:**
```bash
IMAGE_VERSION=secoresearch/fuseki:5.1.0
SPEC=test
CONTAINER_NAME="${SPEC}.fuseki"
STORE_FOLDER="$HOME/tmp/${CONTAINER_NAME}"
TS_PORT=3032
TS_PASSWORD="abc123-qwe"
TS_USERNAME="admin"
UID=1000
GID=1000
```
**2. Start/Stop Fuseki:**
```bash
# Start
cd docker/fuseki
docker compose --env-file .env up fuseki -d
# Stop
# (use the container name from your .env, e.g. test.fuseki)
docker compose stop test.fuseki
```
**3. Access Fuseki:**
- Web interface: http://localhost:3032
- Default dataset: `/test`
- SPARQL endpoint: http://localhost:3032/test/sparql
---
## Neo4j with n10s Plugin Setup
**1. Prepare the environment file:**
```bash
cd docker/neo4j
cp .env.example .env
# Edit with your values
```
**Example `docker/neo4j/.env.example`:**
```bash
IMAGE_VERSION=neo4j:5.20
SPEC=test
CONTAINER_NAME="${SPEC}.sem.neo4j"
NEO4J_PORT=7476
NEO4J_BOLT_PORT=7689
STORE_FOLDER="$HOME/tmp/${CONTAINER_NAME}"
NEO4J_PLUGINS='["apoc", "graph-data-science", "n10s"]'
NEO4J_AUTH="neo4j/test!passfortesting"
```
**2. Start/Stop Neo4j:**
```bash
# Start
cd docker/neo4j
docker compose --env-file .env up neo4j -d
# Stop
docker compose stop neo4j
```
**3. Access Neo4j:**
- Browser: http://localhost:7476
- Username: `neo4j`
- Password: `test!passfortesting`
- Bolt: bolt://localhost:7689
---
## Filesystem Storage (Fallback)
If neither Fuseki nor Neo4j is configured, OntoCast will store ontologies and facts as Turtle files in the working directory.
**No setup required.**
---
## Triple Store Comparison
| Feature | Fuseki | Neo4j + n10s | Filesystem |
|---------|--------|--------------|------------|
| **RDF Native** | ✅ Yes | ⚠️ Via plugin | ✅ Yes |
| **SPARQL** | ✅ Full 1.1 | ❌ Limited | ❌ No |
| **Setup Complexity** | ✅ Simple | ⚠️ Moderate | ✅ Very Simple |
| **Visualization** | ⚠️ Basic | ✅ Excellent | ❌ None |
| **Production Ready** | ✅ Yes | ✅ Yes | ❌ No |
---
## Best Practices
- Use **Filesystem** for quick setup and testing.
- Use **Fuseki** for RDF-focused or production deployments.
- Use **Neo4j** if you need advanced graph analytics or visualization.
- Monitor triple store performance and logs.
- Backup your data regularly.
---
## Troubleshooting
### Fuseki
```bash
# Check if Fuseki is running
curl http://localhost:3032/$/ping
# Restart Fuseki
docker compose restart fuseki
```
### Neo4j
```bash
# Check if Neo4j is running
curl http://localhost:7476
# Check n10s plugin
cypher-shell -u neo4j -p test!passfortesting "CALL n10s.graphconfig.show()"
```
### Common Problems
- **Connection Refused**: Triple store not running
- **Authentication Failed**: Incorrect credentials
- **Dataset Not Found**: Dataset not created in Fuseki
- **Plugin Not Loaded**: n10s plugin not installed in Neo4j

View File

@@ -0,0 +1,9 @@
"""Docker configurations for OntoCast.
This package contains Docker configurations for running OntoCast
dependencies, including triple store databases and other services.
Available configurations:
- neo4j: Neo4j database with n10s plugin
- fuseki: Apache Fuseki triple store server
"""

View File

@@ -0,0 +1,6 @@
IMAGE_VERSION=secoresearch/fuseki:5.1.0
SPEC=ontocast
CONTAINER_NAME="${SPEC}.fuseki"
TS_PORT=3032
TS_PASSWORD="abc123-qwe"
TS_USERNAME="admin"

View File

@@ -0,0 +1,16 @@
services:
fuseki:
image: ${IMAGE_VERSION}
user: "${UID}:${GID}"
restart: "no"
profiles: ["${CONTAINER_NAME}"]
ports:
- "${TS_PORT}:3030"
container_name: ${CONTAINER_NAME}
volumes:
- fuseki_data:/fuseki
environment:
- ADMIN_PASSWORD=${TS_PASSWORD}
volumes:
fuseki_data:
driver: local

View File

@@ -0,0 +1,7 @@
IMAGE_VERSION=neo4j:5.21.0
SPEC=ontocast
CONTAINER_NAME="${SPEC}.neo4j"
NEO4J_PORT=7476
NEO4J_BOLT_PORT=7689
NEO4J_PLUGINS='["n10s"]'
NEO4J_AUTH="neo4j/test!passfortesting"

View File

@@ -0,0 +1,26 @@
services:
neo4j:
image: ${IMAGE_VERSION}
restart: "no"
profiles: ["${CONTAINER_NAME}"]
ports:
- "${NEO4J_PORT}:7474"
- "${NEO4J_BOLT_PORT}:7687"
container_name: ${CONTAINER_NAME}
volumes:
- neo4j_data:/data
- neo4j_plugins:/plugins
- neo4j_logs:/logs
environment:
- NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
- NEO4J_AUTH=${NEO4J_AUTH}
- NEO4J_PLUGINS=${NEO4J_PLUGINS}
- NEO4J_dbms_security_procedures_unrestricted=n10s.*
- NEO4J_dbms_security_procedures_allowlist=n10s.*
volumes:
neo4j_data:
driver: local
neo4j_plugins:
driver: local
neo4j_logs:
driver: local

View File

@@ -0,0 +1,7 @@
IMAGE_VERSION=qdrant/qdrant:v1.16.3
SPEC=ontocast
CONTAINER_NAME="${SPEC}.qdrant"
QDRANT_HTTP_PORT=6333
QDRANT_GRPC_PORT=6334
QDRANT_API_KEY="abc123-qwe"
QDRANT_LOG_LEVEL=INFO

View File

@@ -0,0 +1,18 @@
services:
qdrant:
image: ${IMAGE_VERSION}
restart: "no"
profiles: ["${CONTAINER_NAME}"]
ports:
- "${QDRANT_HTTP_PORT}:6333"
- "${QDRANT_GRPC_PORT}:6334"
container_name: ${CONTAINER_NAME}
volumes:
- qdrant_data:/qdrant/storage
environment:
- QDRANT__SERVICE__API_KEY=${QDRANT_API_KEY}
- QDRANT__LOG_LEVEL=${QDRANT_LOG_LEVEL}
volumes:
qdrant_data:
driver: local

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">
<!-- White circle background -->
<circle cx="48" cy="48" r="45" fill="white"/>
<!-- Original diagram, centered in the white circle -->
<g transform="translate(-2 0)">
<circle cx="65" cy="32" r="15" fill="none" stroke-width="4" stroke="#8b1f6b" />
<circle cx="22" cy="44" r="8" fill="none" stroke-width="4" stroke="#8b1f6b" />
<circle cx="63" cy="73" r="8" fill="none" stroke-width="4" stroke="#8b1f6b" />
<line x1="30" y1="39" x2="49" y2="33" stroke-width="3" stroke="#8b1f6b" stroke-linecap="round" />
<line x1="31" y1="45" x2="51" y2="39" stroke-width="3" stroke="#8b1f6b" stroke-linecap="round" />
<line x1="64" y1="48" x2="63" y2="64" stroke-width="4" stroke="#8b1f6b" stroke-linecap="round" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

View File

@@ -0,0 +1,341 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="355" height="836" viewBox="0 0 355 836">
<defs>
<g>
<g id="glyph-0-0">
<path d="M 6.03125 -2.484375 C 6.03125 -1.703125 5.753906 -1.09375 5.203125 -0.65625 C 4.648438 -0.21875 3.90625 0 2.96875 0 C 2.488281 0 2.039062 -0.03125 1.625 -0.09375 C 1.21875 -0.164062 0.878906 -0.257812 0.609375 -0.375 L 0.609375 -1.421875 C 0.898438 -1.296875 1.257812 -1.179688 1.6875 -1.078125 C 2.113281 -0.972656 2.554688 -0.921875 3.015625 -0.921875 C 3.648438 -0.921875 4.128906 -1.046875 4.453125 -1.296875 C 4.773438 -1.554688 4.9375 -1.90625 4.9375 -2.34375 C 4.9375 -2.625 4.875 -2.863281 4.75 -3.0625 C 4.632812 -3.257812 4.429688 -3.441406 4.140625 -3.609375 C 3.847656 -3.773438 3.441406 -3.953125 2.921875 -4.140625 C 2.203125 -4.410156 1.65625 -4.742188 1.28125 -5.140625 C 0.90625 -5.535156 0.71875 -6.078125 0.71875 -6.765625 C 0.71875 -7.222656 0.832031 -7.617188 1.0625 -7.953125 C 1.300781 -8.285156 1.625 -8.539062 2.03125 -8.71875 C 2.445312 -8.90625 2.921875 -9 3.453125 -9 C 3.921875 -9 4.351562 -8.957031 4.75 -8.875 C 5.144531 -8.789062 5.5 -8.675781 5.8125 -8.53125 L 5.484375 -7.515625 C 5.191406 -7.671875 4.867188 -7.800781 4.515625 -7.90625 C 4.171875 -8.019531 3.8125 -8.078125 3.4375 -8.078125 C 2.894531 -8.078125 2.488281 -7.957031 2.21875 -7.71875 C 1.945312 -7.488281 1.8125 -7.179688 1.8125 -6.796875 C 1.8125 -6.492188 1.867188 -6.242188 1.984375 -6.046875 C 2.109375 -5.859375 2.304688 -5.6875 2.578125 -5.53125 C 2.847656 -5.375 3.21875 -5.203125 3.6875 -5.015625 C 4.1875 -4.828125 4.609375 -4.625 4.953125 -4.40625 C 5.304688 -4.1875 5.570312 -3.925781 5.75 -3.625 C 5.9375 -3.320312 6.03125 -2.941406 6.03125 -2.484375 Z M 6.03125 -2.484375 "/>
</g>
<g id="glyph-0-1">
<path d="M 3.875 0 L 2.796875 0 L 2.796875 -8.078125 L 0.125 -8.078125 L 0.125 -9 L 6.546875 -9 L 6.546875 -8.078125 L 3.875 -8.078125 Z M 3.875 0 "/>
</g>
<g id="glyph-0-2">
<path d="M 6.546875 0 L 5.515625 -2.90625 L 2.109375 -2.90625 L 1.09375 0 L 0 0 L 3.34375 -9 L 4.3125 -9 L 7.65625 0 Z M 4.21875 -6.53125 C 4.195312 -6.59375 4.15625 -6.707031 4.09375 -6.875 C 4.039062 -7.039062 3.988281 -7.210938 3.9375 -7.390625 C 3.882812 -7.578125 3.84375 -7.71875 3.8125 -7.8125 C 3.757812 -7.5625 3.695312 -7.316406 3.625 -7.078125 C 3.550781 -6.847656 3.488281 -6.664062 3.4375 -6.53125 L 2.46875 -3.921875 L 5.1875 -3.921875 Z M 4.21875 -6.53125 "/>
</g>
<g id="glyph-0-3">
<path d="M 3.53125 -9 C 4.59375 -9 5.375 -8.800781 5.875 -8.40625 C 6.382812 -8.007812 6.640625 -7.410156 6.640625 -6.609375 C 6.640625 -6.160156 6.554688 -5.785156 6.390625 -5.484375 C 6.222656 -5.191406 6.007812 -4.957031 5.75 -4.78125 C 5.488281 -4.601562 5.21875 -4.460938 4.9375 -4.359375 L 7.28125 0 L 6.03125 0 L 3.953125 -4.046875 L 2.25 -4.046875 L 2.25 0 L 1.15625 0 L 1.15625 -9 Z M 3.46875 -8.078125 L 2.25 -8.078125 L 2.25 -4.96875 L 3.53125 -4.96875 C 4.226562 -4.96875 4.734375 -5.101562 5.046875 -5.375 C 5.367188 -5.644531 5.53125 -6.039062 5.53125 -6.5625 C 5.53125 -7.113281 5.363281 -7.503906 5.03125 -7.734375 C 4.695312 -7.960938 4.175781 -8.078125 3.46875 -8.078125 Z M 3.46875 -8.078125 "/>
</g>
<g id="glyph-0-4">
<path d="M 4.84375 -8.078125 C 3.914062 -8.078125 3.1875 -7.757812 2.65625 -7.125 C 2.132812 -6.488281 1.875 -5.613281 1.875 -4.5 C 1.875 -3.394531 2.117188 -2.519531 2.609375 -1.875 C 3.097656 -1.238281 3.835938 -0.921875 4.828125 -0.921875 C 5.203125 -0.921875 5.554688 -0.953125 5.890625 -1.015625 C 6.222656 -1.085938 6.550781 -1.171875 6.875 -1.265625 L 6.875 -0.328125 C 6.550781 -0.210938 6.21875 -0.128906 5.875 -0.078125 C 5.539062 -0.0234375 5.140625 0 4.671875 0 C 3.796875 0 3.066406 -0.179688 2.484375 -0.546875 C 1.898438 -0.921875 1.460938 -1.445312 1.171875 -2.125 C 0.878906 -2.800781 0.734375 -3.597656 0.734375 -4.515625 C 0.734375 -5.398438 0.890625 -6.175781 1.203125 -6.84375 C 1.523438 -7.519531 1.992188 -8.046875 2.609375 -8.421875 C 3.222656 -8.804688 3.96875 -9 4.84375 -9 C 5.75 -9 6.539062 -8.835938 7.21875 -8.515625 L 6.78125 -7.484375 C 6.519531 -7.640625 6.222656 -7.773438 5.890625 -7.890625 C 5.566406 -8.015625 5.21875 -8.078125 4.84375 -8.078125 Z M 4.84375 -8.078125 "/>
</g>
<g id="glyph-0-5">
<path d="M 6.609375 -3.515625 C 6.609375 -2.398438 6.335938 -1.535156 5.796875 -0.921875 C 5.253906 -0.304688 4.523438 0 3.609375 0 C 3.046875 0 2.539062 -0.132812 2.09375 -0.40625 C 1.644531 -0.675781 1.289062 -1.070312 1.03125 -1.59375 C 0.78125 -2.125 0.65625 -2.765625 0.65625 -3.515625 C 0.65625 -4.628906 0.921875 -5.488281 1.453125 -6.09375 C 1.992188 -6.695312 2.722656 -7 3.640625 -7 C 4.234375 -7 4.75 -6.863281 5.1875 -6.59375 C 5.632812 -6.320312 5.984375 -5.925781 6.234375 -5.40625 C 6.484375 -4.894531 6.609375 -4.265625 6.609375 -3.515625 Z M 1.75 -3.515625 C 1.75 -2.722656 1.898438 -2.09375 2.203125 -1.625 C 2.503906 -1.15625 2.984375 -0.921875 3.640625 -0.921875 C 4.285156 -0.921875 4.757812 -1.15625 5.0625 -1.625 C 5.363281 -2.09375 5.515625 -2.722656 5.515625 -3.515625 C 5.515625 -4.316406 5.363281 -4.941406 5.0625 -5.390625 C 4.757812 -5.847656 4.28125 -6.078125 3.625 -6.078125 C 2.96875 -6.078125 2.488281 -5.847656 2.1875 -5.390625 C 1.894531 -4.941406 1.75 -4.316406 1.75 -3.515625 Z M 1.75 -3.515625 "/>
</g>
<g id="glyph-0-6">
<path d="M 4.109375 -7 C 4.878906 -7 5.457031 -6.800781 5.84375 -6.40625 C 6.238281 -6.007812 6.4375 -5.363281 6.4375 -4.46875 L 6.4375 0 L 5.40625 0 L 5.40625 -4.421875 C 5.40625 -5.523438 4.921875 -6.078125 3.953125 -6.078125 C 3.242188 -6.078125 2.753906 -5.863281 2.484375 -5.4375 C 2.210938 -5.007812 2.078125 -4.390625 2.078125 -3.578125 L 2.078125 0 L 1.015625 0 L 1.015625 -7 L 1.875 -7 L 2.03125 -5.9375 L 2.09375 -5.9375 C 2.300781 -6.289062 2.585938 -6.554688 2.953125 -6.734375 C 3.316406 -6.910156 3.703125 -7 4.109375 -7 Z M 4.109375 -7 "/>
</g>
<g id="glyph-0-7">
<path d="M 2.4375 0 L 0 -7 L 1.125 -7 L 2.5 -2.875 C 2.5625 -2.675781 2.628906 -2.457031 2.703125 -2.21875 C 2.773438 -1.988281 2.835938 -1.765625 2.890625 -1.546875 C 2.941406 -1.335938 2.984375 -1.160156 3.015625 -1.015625 L 3.0625 -1.015625 C 3.09375 -1.160156 3.132812 -1.34375 3.1875 -1.5625 C 3.25 -1.78125 3.316406 -2.003906 3.390625 -2.234375 C 3.472656 -2.472656 3.539062 -2.6875 3.59375 -2.875 L 4.96875 -7 L 6.09375 -7 L 3.640625 0 Z M 2.4375 0 "/>
</g>
<g id="glyph-0-8">
<path d="M 3.5 -7 C 4.050781 -7 4.523438 -6.863281 4.921875 -6.59375 C 5.316406 -6.332031 5.617188 -5.957031 5.828125 -5.46875 C 6.046875 -4.976562 6.15625 -4.40625 6.15625 -3.75 L 6.15625 -3.046875 L 1.75 -3.046875 C 1.769531 -2.347656 1.957031 -1.816406 2.3125 -1.453125 C 2.664062 -1.097656 3.160156 -0.921875 3.796875 -0.921875 C 4.210938 -0.921875 4.578125 -0.953125 4.890625 -1.015625 C 5.203125 -1.085938 5.53125 -1.191406 5.875 -1.328125 L 5.875 -0.40625 C 5.539062 -0.269531 5.210938 -0.164062 4.890625 -0.09375 C 4.578125 -0.03125 4.195312 0 3.75 0 C 3.144531 0 2.609375 -0.128906 2.140625 -0.390625 C 1.671875 -0.648438 1.304688 -1.035156 1.046875 -1.546875 C 0.785156 -2.066406 0.65625 -2.703125 0.65625 -3.453125 C 0.65625 -4.191406 0.773438 -4.820312 1.015625 -5.34375 C 1.253906 -5.875 1.585938 -6.28125 2.015625 -6.5625 C 2.441406 -6.851562 2.9375 -7 3.5 -7 Z M 3.484375 -6.078125 C 2.984375 -6.078125 2.585938 -5.890625 2.296875 -5.515625 C 2.003906 -5.148438 1.832031 -4.632812 1.78125 -3.96875 L 5.046875 -3.96875 C 5.046875 -4.59375 4.921875 -5.097656 4.671875 -5.484375 C 4.421875 -5.878906 4.023438 -6.078125 3.484375 -6.078125 Z M 3.484375 -6.078125 "/>
</g>
<g id="glyph-0-9">
<path d="M 4.015625 -7 C 4.140625 -7 4.269531 -6.992188 4.40625 -6.984375 C 4.550781 -6.972656 4.675781 -6.957031 4.78125 -6.9375 L 4.640625 -5.984375 C 4.535156 -6.015625 4.421875 -6.035156 4.296875 -6.046875 C 4.171875 -6.066406 4.054688 -6.078125 3.953125 -6.078125 C 3.617188 -6.078125 3.304688 -5.976562 3.015625 -5.78125 C 2.734375 -5.582031 2.503906 -5.304688 2.328125 -4.953125 C 2.160156 -4.609375 2.078125 -4.203125 2.078125 -3.734375 L 2.078125 0 L 1.015625 0 L 1.015625 -7 L 1.890625 -7 L 2 -5.703125 L 2.046875 -5.703125 C 2.253906 -6.054688 2.519531 -6.359375 2.84375 -6.609375 C 3.175781 -6.867188 3.566406 -7 4.015625 -7 Z M 4.015625 -7 "/>
</g>
<g id="glyph-0-10">
<path d="M 3.171875 -0.921875 C 3.328125 -0.921875 3.488281 -0.929688 3.65625 -0.953125 C 3.820312 -0.984375 3.957031 -1.015625 4.0625 -1.046875 L 4.0625 -0.1875 C 3.957031 -0.132812 3.800781 -0.0859375 3.59375 -0.046875 C 3.382812 -0.015625 3.179688 0 2.984375 0 C 2.648438 0 2.335938 -0.0625 2.046875 -0.1875 C 1.765625 -0.3125 1.535156 -0.523438 1.359375 -0.828125 C 1.191406 -1.128906 1.109375 -1.554688 1.109375 -2.109375 L 1.109375 -6.078125 L 0.1875 -6.078125 L 0.1875 -6.65625 L 1.109375 -7.09375 L 1.53125 -8.5 L 2.15625 -8.5 L 2.15625 -7 L 4.015625 -7 L 4.015625 -6.078125 L 2.15625 -6.078125 L 2.15625 -2.140625 C 2.15625 -1.722656 2.25 -1.414062 2.4375 -1.21875 C 2.625 -1.019531 2.867188 -0.921875 3.171875 -0.921875 Z M 3.171875 -0.921875 "/>
</g>
<g id="glyph-0-11">
</g>
<g id="glyph-0-12">
<path d="M 4.9375 0 L 2.125 -7.90625 L 2.078125 -7.90625 C 2.097656 -7.644531 2.113281 -7.289062 2.125 -6.84375 C 2.144531 -6.40625 2.15625 -5.945312 2.15625 -5.46875 L 2.15625 0 L 1.15625 0 L 1.15625 -9 L 2.765625 -9 L 5.40625 -1.609375 L 5.453125 -1.609375 L 8.140625 -9 L 9.71875 -9 L 9.71875 0 L 8.65625 0 L 8.65625 -5.546875 C 8.65625 -5.984375 8.664062 -6.414062 8.6875 -6.84375 C 8.707031 -7.269531 8.722656 -7.617188 8.734375 -7.890625 L 8.6875 -7.890625 L 5.828125 0 Z M 4.9375 0 "/>
</g>
<g id="glyph-0-13">
<path d="M 3.453125 -7 C 4.234375 -7 4.8125 -6.8125 5.1875 -6.4375 C 5.570312 -6.070312 5.765625 -5.488281 5.765625 -4.6875 L 5.765625 0 L 4.984375 0 L 4.78125 -1.078125 L 4.734375 -1.078125 C 4.453125 -0.710938 4.15625 -0.441406 3.84375 -0.265625 C 3.539062 -0.0859375 3.117188 0 2.578125 0 C 1.992188 0 1.507812 -0.15625 1.125 -0.46875 C 0.738281 -0.78125 0.546875 -1.269531 0.546875 -1.9375 C 0.546875 -2.582031 0.796875 -3.082031 1.296875 -3.4375 C 1.804688 -3.789062 2.585938 -3.96875 3.640625 -3.96875 L 4.734375 -3.96875 L 4.734375 -4.421875 C 4.734375 -5.046875 4.613281 -5.476562 4.375 -5.71875 C 4.144531 -5.957031 3.816406 -6.078125 3.390625 -6.078125 C 3.054688 -6.078125 2.738281 -6.019531 2.4375 -5.90625 C 2.132812 -5.789062 1.847656 -5.660156 1.578125 -5.515625 L 1.265625 -6.390625 C 1.546875 -6.554688 1.875 -6.695312 2.25 -6.8125 C 2.632812 -6.9375 3.035156 -7 3.453125 -7 Z M 3.765625 -3.046875 C 2.960938 -3.046875 2.40625 -2.941406 2.09375 -2.734375 C 1.789062 -2.535156 1.640625 -2.25 1.640625 -1.875 C 1.640625 -1.539062 1.75 -1.296875 1.96875 -1.140625 C 2.1875 -0.992188 2.46875 -0.921875 2.8125 -0.921875 C 3.363281 -0.921875 3.816406 -1.050781 4.171875 -1.3125 C 4.535156 -1.582031 4.71875 -1.992188 4.71875 -2.546875 L 4.71875 -3.046875 Z M 3.765625 -3.046875 "/>
</g>
<g id="glyph-0-14">
<path d="M 2.0625 -4.75 C 2.0625 -4.601562 2.054688 -4.414062 2.046875 -4.1875 C 2.035156 -3.957031 2.023438 -3.757812 2.015625 -3.59375 L 2.0625 -3.59375 C 2.113281 -3.664062 2.1875 -3.765625 2.28125 -3.890625 C 2.375 -4.023438 2.46875 -4.160156 2.5625 -4.296875 C 2.664062 -4.429688 2.753906 -4.539062 2.828125 -4.625 L 4.890625 -7 L 6.125 -7 L 3.515625 -4 L 6.296875 0 L 5.03125 0 L 2.796875 -3.25 L 2.0625 -2.5625 L 2.0625 0 L 1.015625 0 L 1.015625 -10 L 2.0625 -10 Z M 2.0625 -4.75 "/>
</g>
<g id="glyph-0-15">
<path d="M 3.296875 0 C 2.492188 0 1.851562 -0.289062 1.375 -0.875 C 0.894531 -1.457031 0.65625 -2.328125 0.65625 -3.484375 C 0.65625 -4.640625 0.894531 -5.515625 1.375 -6.109375 C 1.863281 -6.703125 2.507812 -7 3.3125 -7 C 3.8125 -7 4.21875 -6.894531 4.53125 -6.6875 C 4.84375 -6.488281 5.097656 -6.242188 5.296875 -5.953125 L 5.375 -5.953125 C 5.363281 -6.066406 5.347656 -6.242188 5.328125 -6.484375 C 5.304688 -6.722656 5.296875 -6.910156 5.296875 -7.046875 L 5.296875 -10 L 6.359375 -10 L 6.359375 0 L 5.515625 0 L 5.359375 -1.03125 L 5.296875 -1.03125 C 5.109375 -0.75 4.851562 -0.503906 4.53125 -0.296875 C 4.21875 -0.0976562 3.804688 0 3.296875 0 Z M 3.46875 -0.921875 C 4.144531 -0.921875 4.617188 -1.113281 4.890625 -1.5 C 5.171875 -1.894531 5.3125 -2.488281 5.3125 -3.28125 L 5.3125 -3.46875 C 5.3125 -4.3125 5.179688 -4.957031 4.921875 -5.40625 C 4.660156 -5.851562 4.171875 -6.078125 3.453125 -6.078125 C 2.890625 -6.078125 2.460938 -5.835938 2.171875 -5.359375 C 1.890625 -4.890625 1.75 -4.257812 1.75 -3.46875 C 1.75 -2.65625 1.890625 -2.023438 2.171875 -1.578125 C 2.460938 -1.140625 2.894531 -0.921875 3.46875 -0.921875 Z M 3.46875 -0.921875 "/>
</g>
<g id="glyph-0-16">
<path d="M 5.171875 -3.9375 C 5.066406 -4.289062 4.972656 -4.640625 4.890625 -4.984375 C 4.816406 -5.328125 4.765625 -5.59375 4.734375 -5.78125 L 4.6875 -5.78125 C 4.644531 -5.59375 4.585938 -5.328125 4.515625 -4.984375 C 4.453125 -4.640625 4.363281 -4.285156 4.25 -3.921875 L 3.09375 0 L 1.890625 0 L 0.125 -7 L 1.21875 -7 L 2.109375 -3.25 C 2.203125 -2.875 2.285156 -2.492188 2.359375 -2.109375 C 2.441406 -1.722656 2.5 -1.40625 2.53125 -1.15625 L 2.578125 -1.15625 C 2.609375 -1.300781 2.644531 -1.484375 2.6875 -1.703125 C 2.738281 -1.921875 2.789062 -2.148438 2.84375 -2.390625 C 2.894531 -2.628906 2.953125 -2.847656 3.015625 -3.046875 L 4.15625 -7 L 5.296875 -7 L 6.40625 -3.046875 C 6.5 -2.742188 6.585938 -2.414062 6.671875 -2.0625 C 6.753906 -1.71875 6.8125 -1.421875 6.84375 -1.171875 L 6.890625 -1.171875 C 6.910156 -1.390625 6.960938 -1.691406 7.046875 -2.078125 C 7.128906 -2.460938 7.21875 -2.851562 7.3125 -3.25 L 8.21875 -7 L 9.296875 -7 L 7.515625 0 L 6.28125 0 Z M 5.171875 -3.9375 "/>
</g>
<g id="glyph-0-17">
<path d="M 2.078125 -6.953125 C 2.078125 -6.585938 2.054688 -6.242188 2.015625 -5.921875 L 2.09375 -5.921875 C 2.300781 -6.273438 2.582031 -6.539062 2.9375 -6.71875 C 3.289062 -6.90625 3.675781 -7 4.09375 -7 C 4.875 -7 5.457031 -6.800781 5.84375 -6.40625 C 6.238281 -6.007812 6.4375 -5.367188 6.4375 -4.484375 L 6.4375 0 L 5.40625 0 L 5.40625 -4.421875 C 5.40625 -5.523438 4.921875 -6.078125 3.953125 -6.078125 C 3.234375 -6.078125 2.738281 -5.859375 2.46875 -5.421875 C 2.207031 -4.992188 2.078125 -4.375 2.078125 -3.5625 L 2.078125 0 L 1.015625 0 L 1.015625 -10 L 2.078125 -10 Z M 2.078125 -6.953125 "/>
</g>
<g id="glyph-0-18">
<path d="M 6.390625 -7 L 6.390625 0 L 5.53125 0 L 5.375 -1.03125 L 5.328125 -1.03125 C 5.117188 -0.675781 4.828125 -0.414062 4.453125 -0.25 C 4.085938 -0.0820312 3.695312 0 3.28125 0 C 2.507812 0 1.925781 -0.195312 1.53125 -0.59375 C 1.144531 -0.988281 0.953125 -1.625 0.953125 -2.5 L 0.953125 -7 L 2.015625 -7 L 2.015625 -2.5625 C 2.015625 -1.46875 2.488281 -0.921875 3.4375 -0.921875 C 4.15625 -0.921875 4.648438 -1.132812 4.921875 -1.5625 C 5.203125 -1.988281 5.34375 -2.609375 5.34375 -3.421875 L 5.34375 -7 Z M 6.390625 -7 "/>
</g>
<g id="glyph-0-19">
<path d="M 2.546875 -3.578125 L 0.328125 -7 L 1.53125 -7 L 3.1875 -4.34375 L 4.828125 -7 L 6.015625 -7 L 3.796875 -3.5625 L 6.125 0 L 4.9375 0 L 3.1875 -2.78125 L 1.40625 0 L 0.21875 0 Z M 2.546875 -3.578125 "/>
</g>
<g id="glyph-0-20">
<path d="M 2.078125 0 L 1.015625 0 L 1.015625 -10 L 2.078125 -10 Z M 2.078125 0 "/>
</g>
<g id="glyph-0-21">
<path d="M 3.59375 0 C 3.03125 0 2.523438 -0.117188 2.078125 -0.359375 C 1.640625 -0.609375 1.289062 -0.988281 1.03125 -1.5 C 0.78125 -2.007812 0.65625 -2.664062 0.65625 -3.46875 C 0.65625 -4.289062 0.785156 -4.960938 1.046875 -5.484375 C 1.316406 -6.003906 1.675781 -6.382812 2.125 -6.625 C 2.582031 -6.875 3.097656 -7 3.671875 -7 C 3.992188 -7 4.304688 -6.960938 4.609375 -6.890625 C 4.921875 -6.828125 5.171875 -6.75 5.359375 -6.65625 L 5.046875 -5.765625 C 4.847656 -5.835938 4.617188 -5.90625 4.359375 -5.96875 C 4.109375 -6.039062 3.867188 -6.078125 3.640625 -6.078125 C 2.378906 -6.078125 1.75 -5.210938 1.75 -3.484375 C 1.75 -2.648438 1.898438 -2.015625 2.203125 -1.578125 C 2.515625 -1.140625 2.976562 -0.921875 3.59375 -0.921875 C 3.9375 -0.921875 4.242188 -0.957031 4.515625 -1.03125 C 4.785156 -1.113281 5.03125 -1.207031 5.25 -1.3125 L 5.25 -0.34375 C 5.039062 -0.226562 4.804688 -0.140625 4.546875 -0.078125 C 4.285156 -0.0234375 3.96875 0 3.59375 0 Z M 3.59375 0 "/>
</g>
<g id="glyph-0-22">
<path d="M 8.640625 -4.5 C 8.640625 -3.59375 8.488281 -2.800781 8.1875 -2.125 C 7.882812 -1.457031 7.441406 -0.9375 6.859375 -0.5625 C 6.273438 -0.1875 5.550781 0 4.6875 0 C 3.800781 0 3.0625 -0.1875 2.46875 -0.5625 C 1.882812 -0.9375 1.445312 -1.460938 1.15625 -2.140625 C 0.875 -2.816406 0.734375 -3.609375 0.734375 -4.515625 C 0.734375 -5.410156 0.875 -6.191406 1.15625 -6.859375 C 1.445312 -7.535156 1.882812 -8.0625 2.46875 -8.4375 C 3.0625 -8.8125 3.804688 -9 4.703125 -9 C 5.554688 -9 6.273438 -8.8125 6.859375 -8.4375 C 7.441406 -8.070312 7.882812 -7.550781 8.1875 -6.875 C 8.488281 -6.195312 8.640625 -5.40625 8.640625 -4.5 Z M 1.875 -4.515625 C 1.875 -3.398438 2.097656 -2.519531 2.546875 -1.875 C 3.003906 -1.238281 3.71875 -0.921875 4.6875 -0.921875 C 5.664062 -0.921875 6.378906 -1.238281 6.828125 -1.875 C 7.273438 -2.519531 7.5 -3.398438 7.5 -4.515625 C 7.5 -5.628906 7.273438 -6.5 6.828125 -7.125 C 6.378906 -7.757812 5.671875 -8.078125 4.703125 -8.078125 C 3.734375 -8.078125 3.019531 -7.757812 2.5625 -7.125 C 2.101562 -6.5 1.875 -5.628906 1.875 -4.515625 Z M 1.875 -4.515625 "/>
</g>
<g id="glyph-0-23">
<path d="M 3.296875 -7 C 3.722656 -7 4.101562 -6.910156 4.4375 -6.734375 C 4.78125 -6.554688 5.070312 -6.285156 5.3125 -5.921875 L 5.375 -5.921875 L 5.515625 -7 L 6.359375 -7 L 6.359375 0.015625 C 6.359375 1.003906 6.125 1.75 5.65625 2.25 C 5.1875 2.75 4.460938 3 3.484375 3 C 2.535156 3 1.757812 2.863281 1.15625 2.59375 L 1.15625 1.546875 C 1.789062 1.898438 2.585938 2.078125 3.546875 2.078125 C 4.097656 2.078125 4.53125 1.898438 4.84375 1.546875 C 5.15625 1.191406 5.3125 0.710938 5.3125 0.109375 L 5.3125 -0.171875 C 5.3125 -0.273438 5.316406 -0.421875 5.328125 -0.609375 C 5.335938 -0.804688 5.347656 -0.945312 5.359375 -1.03125 L 5.296875 -1.03125 C 4.867188 -0.34375 4.207031 0 3.3125 0 C 2.476562 0 1.828125 -0.304688 1.359375 -0.921875 C 0.890625 -1.535156 0.65625 -2.390625 0.65625 -3.484375 C 0.65625 -4.554688 0.890625 -5.410156 1.359375 -6.046875 C 1.828125 -6.679688 2.472656 -7 3.296875 -7 Z M 3.4375 -6.078125 C 2.90625 -6.078125 2.488281 -5.851562 2.1875 -5.40625 C 1.894531 -4.957031 1.75 -4.316406 1.75 -3.484375 C 1.75 -2.660156 1.894531 -2.023438 2.1875 -1.578125 C 2.476562 -1.140625 2.90625 -0.921875 3.46875 -0.921875 C 4.113281 -0.921875 4.582031 -1.101562 4.875 -1.46875 C 5.175781 -1.832031 5.328125 -2.421875 5.328125 -3.234375 L 5.328125 -3.5 C 5.328125 -4.425781 5.175781 -5.085938 4.875 -5.484375 C 4.570312 -5.878906 4.09375 -6.078125 3.4375 -6.078125 Z M 3.4375 -6.078125 "/>
</g>
<g id="glyph-0-24">
<path d="M 0.015625 -7 L 1.140625 -7 L 2.53125 -3.03125 C 2.65625 -2.675781 2.765625 -2.34375 2.859375 -2.03125 C 2.953125 -1.71875 3.023438 -1.414062 3.078125 -1.125 L 3.125 -1.125 C 3.164062 -1.34375 3.238281 -1.625 3.34375 -1.96875 C 3.445312 -2.320312 3.554688 -2.679688 3.671875 -3.046875 L 4.984375 -7 L 6.125 -7 L 3.34375 0.859375 C 3.125 1.515625 2.832031 2.035156 2.46875 2.421875 C 2.113281 2.804688 1.628906 3 1.015625 3 C 0.816406 3 0.644531 2.988281 0.5 2.96875 C 0.351562 2.957031 0.226562 2.941406 0.125 2.921875 L 0.125 2.015625 C 0.21875 2.023438 0.328125 2.035156 0.453125 2.046875 C 0.578125 2.066406 0.707031 2.078125 0.84375 2.078125 C 1.207031 2.078125 1.503906 1.96875 1.734375 1.75 C 1.960938 1.53125 2.140625 1.238281 2.265625 0.875 L 2.609375 -0.015625 Z M 0.015625 -7 "/>
</g>
<g id="glyph-0-25">
<path d="M 3.609375 -9 C 4.679688 -9 5.488281 -8.828125 6.03125 -8.484375 C 6.582031 -8.140625 6.859375 -7.535156 6.859375 -6.671875 C 6.859375 -6.117188 6.71875 -5.660156 6.4375 -5.296875 C 6.164062 -4.929688 5.765625 -4.695312 5.234375 -4.59375 L 5.234375 -4.53125 C 5.585938 -4.476562 5.910156 -4.375 6.203125 -4.21875 C 6.492188 -4.0625 6.722656 -3.84375 6.890625 -3.5625 C 7.066406 -3.28125 7.15625 -2.910156 7.15625 -2.453125 C 7.15625 -1.671875 6.890625 -1.066406 6.359375 -0.640625 C 5.828125 -0.210938 5.097656 0 4.171875 0 L 1.15625 0 L 1.15625 -9 Z M 3.828125 -4.96875 C 4.566406 -4.96875 5.070312 -5.101562 5.34375 -5.375 C 5.613281 -5.644531 5.75 -6.046875 5.75 -6.578125 C 5.75 -7.109375 5.582031 -7.488281 5.25 -7.71875 C 4.914062 -7.957031 4.382812 -8.078125 3.65625 -8.078125 L 2.25 -8.078125 L 2.25 -4.96875 Z M 2.25 -4.046875 L 2.25 -0.921875 L 3.96875 -0.921875 C 4.726562 -0.921875 5.253906 -1.066406 5.546875 -1.359375 C 5.847656 -1.660156 6 -2.050781 6 -2.53125 C 6 -2.988281 5.84375 -3.351562 5.53125 -3.625 C 5.226562 -3.90625 4.679688 -4.046875 3.890625 -4.046875 Z M 2.25 -4.046875 "/>
</g>
<g id="glyph-0-26">
<path d="M 5.203125 -1.984375 C 5.203125 -1.328125 4.96875 -0.832031 4.5 -0.5 C 4.039062 -0.164062 3.421875 0 2.640625 0 C 2.191406 0 1.804688 -0.0351562 1.484375 -0.109375 C 1.160156 -0.179688 0.875 -0.285156 0.625 -0.421875 L 0.625 -1.453125 C 0.882812 -1.316406 1.191406 -1.191406 1.546875 -1.078125 C 1.910156 -0.972656 2.28125 -0.921875 2.65625 -0.921875 C 3.195312 -0.921875 3.585938 -1.007812 3.828125 -1.1875 C 4.066406 -1.363281 4.1875 -1.601562 4.1875 -1.90625 C 4.1875 -2.070312 4.140625 -2.222656 4.046875 -2.359375 C 3.960938 -2.492188 3.804688 -2.628906 3.578125 -2.765625 C 3.347656 -2.898438 3.023438 -3.050781 2.609375 -3.21875 C 2.191406 -3.382812 1.832031 -3.550781 1.53125 -3.71875 C 1.238281 -3.882812 1.007812 -4.082031 0.84375 -4.3125 C 0.6875 -4.550781 0.609375 -4.851562 0.609375 -5.21875 C 0.609375 -5.789062 0.828125 -6.226562 1.265625 -6.53125 C 1.710938 -6.84375 2.300781 -7 3.03125 -7 C 3.414062 -7 3.773438 -6.957031 4.109375 -6.875 C 4.453125 -6.800781 4.773438 -6.691406 5.078125 -6.546875 L 4.71875 -5.625 C 4.445312 -5.75 4.160156 -5.851562 3.859375 -5.9375 C 3.566406 -6.03125 3.265625 -6.078125 2.953125 -6.078125 C 2.515625 -6.078125 2.179688 -6.003906 1.953125 -5.859375 C 1.734375 -5.710938 1.625 -5.515625 1.625 -5.265625 C 1.625 -5.078125 1.675781 -4.914062 1.78125 -4.78125 C 1.882812 -4.65625 2.054688 -4.53125 2.296875 -4.40625 C 2.535156 -4.289062 2.863281 -4.148438 3.28125 -3.984375 C 3.6875 -3.828125 4.03125 -3.660156 4.3125 -3.484375 C 4.601562 -3.316406 4.820312 -3.113281 4.96875 -2.875 C 5.125 -2.644531 5.203125 -2.347656 5.203125 -1.984375 Z M 5.203125 -1.984375 "/>
</g>
<g id="glyph-0-27">
<path d="M 4.078125 -7 C 4.867188 -7 5.503906 -6.707031 5.984375 -6.125 C 6.472656 -5.550781 6.71875 -4.679688 6.71875 -3.515625 C 6.71875 -2.359375 6.472656 -1.484375 5.984375 -0.890625 C 5.503906 -0.296875 4.863281 0 4.0625 0 C 3.570312 0 3.164062 -0.0976562 2.84375 -0.296875 C 2.519531 -0.492188 2.265625 -0.722656 2.078125 -0.984375 L 2 -0.984375 C 2.019531 -0.835938 2.035156 -0.648438 2.046875 -0.421875 C 2.066406 -0.203125 2.078125 -0.0078125 2.078125 0.15625 L 2.078125 3 L 1.015625 3 L 1.015625 -7 L 1.890625 -7 L 2.03125 -5.90625 L 2.078125 -5.90625 C 2.265625 -6.21875 2.515625 -6.476562 2.828125 -6.6875 C 3.140625 -6.894531 3.554688 -7 4.078125 -7 Z M 3.890625 -6.078125 C 3.234375 -6.078125 2.769531 -5.882812 2.5 -5.5 C 2.226562 -5.113281 2.085938 -4.523438 2.078125 -3.734375 L 2.078125 -3.515625 C 2.078125 -2.679688 2.207031 -2.039062 2.46875 -1.59375 C 2.726562 -1.144531 3.207031 -0.921875 3.90625 -0.921875 C 4.300781 -0.921875 4.625 -1.035156 4.875 -1.265625 C 5.125 -1.492188 5.3125 -1.800781 5.4375 -2.1875 C 5.5625 -2.582031 5.625 -3.03125 5.625 -3.53125 C 5.625 -4.300781 5.484375 -4.914062 5.203125 -5.375 C 4.921875 -5.84375 4.484375 -6.078125 3.890625 -6.078125 Z M 3.890625 -6.078125 "/>
</g>
<g id="glyph-0-28">
<path d="M 7.6875 -3.25 C 7.6875 -2.644531 7.5625 -2.09375 7.3125 -1.59375 C 7.070312 -1.101562 6.707031 -0.710938 6.21875 -0.421875 C 5.726562 -0.140625 5.101562 0 4.34375 0 C 3.28125 0 2.46875 -0.296875 1.90625 -0.890625 C 1.351562 -1.492188 1.078125 -2.289062 1.078125 -3.28125 L 1.078125 -9 L 2.15625 -9 L 2.15625 -3.203125 C 2.15625 -2.484375 2.34375 -1.921875 2.71875 -1.515625 C 3.09375 -1.117188 3.65625 -0.921875 4.40625 -0.921875 C 5.175781 -0.921875 5.734375 -1.132812 6.078125 -1.5625 C 6.429688 -1.988281 6.609375 -2.539062 6.609375 -3.21875 L 6.609375 -9 L 7.6875 -9 Z M 7.6875 -3.25 "/>
</g>
<g id="glyph-0-29">
<path d="M 2.25 0 L 1.15625 0 L 1.15625 -9 L 5.953125 -9 L 5.953125 -8.078125 L 2.25 -8.078125 L 2.25 -4.96875 L 5.71875 -4.96875 L 5.71875 -4.046875 L 2.25 -4.046875 Z M 2.25 0 "/>
</g>
<g id="glyph-0-30">
<path d="M 7.953125 0 L 6.703125 0 L 2.109375 -7.46875 L 2.0625 -7.46875 C 2.082031 -7.175781 2.101562 -6.8125 2.125 -6.375 C 2.144531 -5.9375 2.15625 -5.488281 2.15625 -5.03125 L 2.15625 0 L 1.15625 0 L 1.15625 -9 L 2.40625 -9 L 6.984375 -1.90625 L 7.03125 -1.90625 C 7.019531 -2.039062 7.007812 -2.234375 7 -2.484375 C 6.988281 -2.742188 6.976562 -3.023438 6.96875 -3.328125 C 6.957031 -3.628906 6.953125 -3.910156 6.953125 -4.171875 L 6.953125 -9 L 7.953125 -9 Z M 7.953125 0 "/>
</g>
<g id="glyph-0-31">
<path d="M 8.078125 -7 C 8.804688 -7 9.347656 -6.800781 9.703125 -6.40625 C 10.066406 -6.007812 10.25 -5.363281 10.25 -4.46875 L 10.25 0 L 9.203125 0 L 9.203125 -4.4375 C 9.203125 -5.53125 8.765625 -6.078125 7.890625 -6.078125 C 7.265625 -6.078125 6.816406 -5.882812 6.546875 -5.5 C 6.285156 -5.113281 6.15625 -4.550781 6.15625 -3.8125 L 6.15625 0 L 5.109375 0 L 5.109375 -4.4375 C 5.109375 -5.53125 4.671875 -6.078125 3.796875 -6.078125 C 3.140625 -6.078125 2.6875 -5.863281 2.4375 -5.4375 C 2.195312 -5.007812 2.078125 -4.390625 2.078125 -3.578125 L 2.078125 0 L 1.015625 0 L 1.015625 -7 L 1.875 -7 L 2.03125 -5.9375 L 2.09375 -5.9375 C 2.289062 -6.289062 2.554688 -6.554688 2.890625 -6.734375 C 3.234375 -6.910156 3.597656 -7 3.984375 -7 C 4.992188 -7 5.648438 -6.613281 5.953125 -5.84375 L 6.015625 -5.84375 C 6.234375 -6.238281 6.523438 -6.53125 6.890625 -6.71875 C 7.265625 -6.90625 7.660156 -7 8.078125 -7 Z M 8.078125 -7 "/>
</g>
<g id="glyph-0-32">
<path d="M 1.5625 -10 C 1.71875 -10 1.859375 -9.960938 1.984375 -9.890625 C 2.109375 -9.816406 2.171875 -9.695312 2.171875 -9.53125 C 2.171875 -9.382812 2.109375 -9.269531 1.984375 -9.1875 C 1.859375 -9.113281 1.71875 -9.078125 1.5625 -9.078125 C 1.382812 -9.078125 1.234375 -9.113281 1.109375 -9.1875 C 0.992188 -9.269531 0.9375 -9.382812 0.9375 -9.53125 C 0.9375 -9.695312 0.992188 -9.816406 1.109375 -9.890625 C 1.234375 -9.960938 1.382812 -10 1.5625 -10 Z M 2.078125 -7 L 2.078125 0 L 1.015625 0 L 1.015625 -7 Z M 2.078125 -7 "/>
</g>
<g id="glyph-0-33">
<path d="M 5.171875 0 L 0.46875 0 L 0.46875 -0.796875 L 3.921875 -6.078125 L 0.671875 -6.078125 L 0.671875 -7 L 5.09375 -7 L 5.09375 -6.109375 L 1.6875 -0.921875 L 5.171875 -0.921875 Z M 5.171875 0 "/>
</g>
<g id="glyph-0-34">
<path d="M 5.953125 0 L 1.15625 0 L 1.15625 -9 L 5.953125 -9 L 5.953125 -8.078125 L 2.25 -8.078125 L 2.25 -4.96875 L 5.734375 -4.96875 L 5.734375 -4.046875 L 2.25 -4.046875 L 2.25 -0.921875 L 5.953125 -0.921875 Z M 5.953125 0 "/>
</g>
<g id="glyph-0-35">
<path d="M 8.03125 -4.59375 C 8.03125 -3.070312 7.632812 -1.925781 6.84375 -1.15625 C 6.050781 -0.382812 4.953125 0 3.546875 0 L 1.15625 0 L 1.15625 -9 L 3.796875 -9 C 4.660156 -9 5.410156 -8.828125 6.046875 -8.484375 C 6.679688 -8.148438 7.171875 -7.65625 7.515625 -7 C 7.859375 -6.351562 8.03125 -5.550781 8.03125 -4.59375 Z M 6.890625 -4.546875 C 6.890625 -5.765625 6.609375 -6.65625 6.046875 -7.21875 C 5.484375 -7.789062 4.679688 -8.078125 3.640625 -8.078125 L 2.25 -8.078125 L 2.25 -0.921875 L 3.40625 -0.921875 C 5.726562 -0.921875 6.890625 -2.128906 6.890625 -4.546875 Z M 6.890625 -4.546875 "/>
</g>
</g>
</defs>
<path fill-rule="nonzero" fill="rgb(100%, 85.882353%, 60%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 265.085938 -810 C 265.085938 -800.058594 251.078125 -792 233.796875 -792 C 216.515625 -792 202.503906 -800.058594 202.503906 -810 C 202.503906 -819.941406 216.515625 -828 233.796875 -828 C 251.078125 -828 265.085938 -819.941406 265.085938 -810 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-0" x="220.296875" y="25.101562"/>
<use xlink:href="#glyph-0-1" x="227.046875" y="25.101562"/>
<use xlink:href="#glyph-0-2" x="233.046875" y="25.101562"/>
<use xlink:href="#glyph-0-3" x="240.546875" y="25.101562"/>
<use xlink:href="#glyph-0-1" x="248.046875" y="25.101562"/>
</g>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 320.390625 -738 C 320.390625 -728.058594 281.621094 -720 233.796875 -720 C 185.96875 -720 147.199219 -728.058594 147.199219 -738 C 147.199219 -747.941406 185.96875 -756 233.796875 -756 C 281.621094 -756 320.390625 -747.941406 320.390625 -738 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-4" x="175.296875" y="97.101562"/>
<use xlink:href="#glyph-0-5" x="182.796875" y="97.101562"/>
<use xlink:href="#glyph-0-6" x="190.296875" y="97.101562"/>
<use xlink:href="#glyph-0-7" x="197.796875" y="97.101562"/>
<use xlink:href="#glyph-0-8" x="203.796875" y="97.101562"/>
<use xlink:href="#glyph-0-9" x="210.546875" y="97.101562"/>
<use xlink:href="#glyph-0-10" x="215.796875" y="97.101562"/>
<use xlink:href="#glyph-0-11" x="220.296875" y="97.101562"/>
<use xlink:href="#glyph-0-10" x="223.296875" y="97.101562"/>
<use xlink:href="#glyph-0-5" x="227.796875" y="97.101562"/>
<use xlink:href="#glyph-0-11" x="235.296875" y="97.101562"/>
<use xlink:href="#glyph-0-12" x="238.296875" y="97.101562"/>
<use xlink:href="#glyph-0-13" x="249.546875" y="97.101562"/>
<use xlink:href="#glyph-0-9" x="256.296875" y="97.101562"/>
<use xlink:href="#glyph-0-14" x="261.546875" y="97.101562"/>
<use xlink:href="#glyph-0-15" x="268.296875" y="97.101562"/>
<use xlink:href="#glyph-0-5" x="275.796875" y="97.101562"/>
<use xlink:href="#glyph-0-16" x="282.546875" y="97.101562"/>
<use xlink:href="#glyph-0-6" x="292.296875" y="97.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 233.796875 -791.695312 C 233.796875 -783.984375 233.796875 -774.710938 233.796875 -766.113281 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 237.296875 -766.105469 L 233.796875 -756.105469 L 230.296875 -766.105469 Z M 237.296875 -766.105469 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 283.289062 -666 C 283.289062 -656.058594 261.128906 -648 233.796875 -648 C 206.460938 -648 184.300781 -656.058594 184.300781 -666 C 184.300781 -675.941406 206.460938 -684 233.796875 -684 C 261.128906 -684 283.289062 -675.941406 283.289062 -666 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-4" x="205.796875" y="169.101562"/>
<use xlink:href="#glyph-0-17" x="213.296875" y="169.101562"/>
<use xlink:href="#glyph-0-18" x="220.796875" y="169.101562"/>
<use xlink:href="#glyph-0-6" x="228.296875" y="169.101562"/>
<use xlink:href="#glyph-0-14" x="235.796875" y="169.101562"/>
<use xlink:href="#glyph-0-11" x="242.546875" y="169.101562"/>
<use xlink:href="#glyph-0-1" x="245.546875" y="169.101562"/>
<use xlink:href="#glyph-0-8" x="251.546875" y="169.101562"/>
<use xlink:href="#glyph-0-19" x="258.296875" y="169.101562"/>
<use xlink:href="#glyph-0-10" x="264.296875" y="169.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 233.796875 -719.695312 C 233.796875 -711.984375 233.796875 -702.710938 233.796875 -694.113281 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 237.296875 -694.105469 L 233.796875 -684.105469 L 230.296875 -694.105469 Z M 237.296875 -694.105469 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 298.800781 -594 C 298.800781 -584.058594 269.695312 -576 233.796875 -576 C 197.894531 -576 168.789062 -584.058594 168.789062 -594 C 168.789062 -603.941406 197.894531 -612 233.796875 -612 C 269.695312 -612 298.800781 -603.941406 298.800781 -594 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-0" x="192.796875" y="241.101562"/>
<use xlink:href="#glyph-0-8" x="199.546875" y="241.101562"/>
<use xlink:href="#glyph-0-20" x="206.296875" y="241.101562"/>
<use xlink:href="#glyph-0-8" x="209.296875" y="241.101562"/>
<use xlink:href="#glyph-0-21" x="216.046875" y="241.101562"/>
<use xlink:href="#glyph-0-10" x="222.046875" y="241.101562"/>
<use xlink:href="#glyph-0-11" x="226.546875" y="241.101562"/>
<use xlink:href="#glyph-0-22" x="229.546875" y="241.101562"/>
<use xlink:href="#glyph-0-6" x="238.546875" y="241.101562"/>
<use xlink:href="#glyph-0-10" x="246.046875" y="241.101562"/>
<use xlink:href="#glyph-0-5" x="250.546875" y="241.101562"/>
<use xlink:href="#glyph-0-20" x="258.046875" y="241.101562"/>
<use xlink:href="#glyph-0-5" x="261.046875" y="241.101562"/>
<use xlink:href="#glyph-0-23" x="268.546875" y="241.101562"/>
<use xlink:href="#glyph-0-24" x="276.046875" y="241.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 233.796875 -647.695312 C 233.796875 -639.984375 233.796875 -630.710938 233.796875 -622.113281 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 237.296875 -622.105469 L 233.796875 -612.105469 L 230.296875 -622.105469 Z M 237.296875 -622.105469 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 157.386719 -522 C 157.386719 -512.058594 122.199219 -504 78.796875 -504 C 35.390625 -504 0.203125 -512.058594 0.203125 -522 C 0.203125 -531.941406 35.390625 -540 78.796875 -540 C 122.199219 -540 157.386719 -531.941406 157.386719 -522 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-25" x="26.796875" y="313.101562"/>
<use xlink:href="#glyph-0-5" x="34.296875" y="313.101562"/>
<use xlink:href="#glyph-0-5" x="41.796875" y="313.101562"/>
<use xlink:href="#glyph-0-10" x="49.296875" y="313.101562"/>
<use xlink:href="#glyph-0-26" x="53.796875" y="313.101562"/>
<use xlink:href="#glyph-0-10" x="59.796875" y="313.101562"/>
<use xlink:href="#glyph-0-9" x="64.296875" y="313.101562"/>
<use xlink:href="#glyph-0-13" x="68.796875" y="313.101562"/>
<use xlink:href="#glyph-0-27" x="75.546875" y="313.101562"/>
<use xlink:href="#glyph-0-11" x="83.046875" y="313.101562"/>
<use xlink:href="#glyph-0-22" x="86.046875" y="313.101562"/>
<use xlink:href="#glyph-0-6" x="95.046875" y="313.101562"/>
<use xlink:href="#glyph-0-10" x="102.546875" y="313.101562"/>
<use xlink:href="#glyph-0-5" x="107.046875" y="313.101562"/>
<use xlink:href="#glyph-0-20" x="114.546875" y="313.101562"/>
<use xlink:href="#glyph-0-5" x="117.546875" y="313.101562"/>
<use xlink:href="#glyph-0-23" x="125.046875" y="313.101562"/>
<use xlink:href="#glyph-0-24" x="132.546875" y="313.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-dasharray="6" stroke-miterlimit="10" d="M 201.300781 -578.324219 C 178.316406 -567.945312 147.25 -553.914062 122.152344 -542.582031 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 123.410156 -539.308594 L 112.855469 -538.382812 L 120.527344 -545.6875 Z M 123.410156 -539.308594 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 218.148438 -450 C 218.148438 -440.058594 186.652344 -432 147.796875 -432 C 108.941406 -432 77.441406 -440.058594 77.441406 -450 C 77.441406 -459.941406 108.941406 -468 147.796875 -468 C 186.652344 -468 218.148438 -459.941406 218.148438 -450 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-28" x="102.796875" y="385.101563"/>
<use xlink:href="#glyph-0-27" x="111.796875" y="385.101563"/>
<use xlink:href="#glyph-0-15" x="119.296875" y="385.101563"/>
<use xlink:href="#glyph-0-13" x="126.796875" y="385.101563"/>
<use xlink:href="#glyph-0-10" x="133.546875" y="385.101563"/>
<use xlink:href="#glyph-0-8" x="138.046875" y="385.101563"/>
<use xlink:href="#glyph-0-11" x="144.796875" y="385.101563"/>
<use xlink:href="#glyph-0-22" x="147.796875" y="385.101563"/>
<use xlink:href="#glyph-0-6" x="156.796875" y="385.101563"/>
<use xlink:href="#glyph-0-10" x="164.296875" y="385.101563"/>
<use xlink:href="#glyph-0-5" x="168.796875" y="385.101563"/>
<use xlink:href="#glyph-0-20" x="176.296875" y="385.101563"/>
<use xlink:href="#glyph-0-5" x="179.296875" y="385.101563"/>
<use xlink:href="#glyph-0-23" x="186.796875" y="385.101563"/>
<use xlink:href="#glyph-0-24" x="194.296875" y="385.101563"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-dasharray="6" stroke-miterlimit="10" d="M 224.519531 -576.082031 C 214.53125 -558.039062 198.015625 -528.714844 182.796875 -504 C 177.214844 -494.941406 170.871094 -485.183594 165.128906 -476.539062 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 167.890625 -474.371094 L 159.421875 -468.007812 L 162.074219 -478.265625 Z M 167.890625 -474.371094 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 347.101562 -234 C 347.101562 -224.058594 322.339844 -216 291.796875 -216 C 261.25 -216 236.488281 -224.058594 236.488281 -234 C 236.488281 -243.941406 261.25 -252 291.796875 -252 C 322.339844 -252 347.101562 -243.941406 347.101562 -234 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-3" x="258.796875" y="601.101562"/>
<use xlink:href="#glyph-0-8" x="266.296875" y="601.101562"/>
<use xlink:href="#glyph-0-6" x="273.046875" y="601.101562"/>
<use xlink:href="#glyph-0-15" x="280.546875" y="601.101562"/>
<use xlink:href="#glyph-0-8" x="288.046875" y="601.101562"/>
<use xlink:href="#glyph-0-9" x="294.796875" y="601.101562"/>
<use xlink:href="#glyph-0-11" x="300.046875" y="601.101562"/>
<use xlink:href="#glyph-0-29" x="303.046875" y="601.101562"/>
<use xlink:href="#glyph-0-13" x="309.046875" y="601.101562"/>
<use xlink:href="#glyph-0-21" x="315.796875" y="601.101562"/>
<use xlink:href="#glyph-0-10" x="321.796875" y="601.101562"/>
<use xlink:href="#glyph-0-26" x="326.296875" y="601.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-dasharray="6" stroke-miterlimit="10" d="M 255.925781 -576.933594 C 285.925781 -553.109375 335.796875 -505.332031 335.796875 -451 C 335.796875 -451 335.796875 -451 335.796875 -377 C 335.796875 -334.980469 318 -288.976562 305.042969 -261.050781 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 308.167969 -259.472656 L 300.699219 -251.960938 L 301.855469 -262.492188 Z M 308.167969 -259.472656 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 95.5 -504.054688 C 104.058594 -495.371094 114.632812 -484.644531 124.027344 -475.113281 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 126.71875 -477.367188 L 131.246094 -467.789062 L 121.734375 -472.453125 Z M 126.71875 -477.367188 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 259.867188 -378 C 259.867188 -368.058594 209.691406 -360 147.796875 -360 C 85.898438 -360 35.722656 -368.058594 35.722656 -378 C 35.722656 -387.941406 85.898438 -396 147.796875 -396 C 209.691406 -396 259.867188 -387.941406 259.867188 -378 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-30" x="68.796875" y="457.101563"/>
<use xlink:href="#glyph-0-5" x="77.796875" y="457.101563"/>
<use xlink:href="#glyph-0-9" x="85.296875" y="457.101563"/>
<use xlink:href="#glyph-0-31" x="90.546875" y="457.101563"/>
<use xlink:href="#glyph-0-13" x="101.796875" y="457.101563"/>
<use xlink:href="#glyph-0-20" x="108.546875" y="457.101563"/>
<use xlink:href="#glyph-0-32" x="111.546875" y="457.101563"/>
<use xlink:href="#glyph-0-33" x="114.546875" y="457.101563"/>
<use xlink:href="#glyph-0-8" x="120.546875" y="457.101563"/>
<use xlink:href="#glyph-0-11" x="127.296875" y="457.101563"/>
<use xlink:href="#glyph-0-22" x="130.296875" y="457.101563"/>
<use xlink:href="#glyph-0-6" x="139.296875" y="457.101563"/>
<use xlink:href="#glyph-0-10" x="146.796875" y="457.101563"/>
<use xlink:href="#glyph-0-5" x="151.296875" y="457.101563"/>
<use xlink:href="#glyph-0-20" x="158.796875" y="457.101563"/>
<use xlink:href="#glyph-0-5" x="161.796875" y="457.101563"/>
<use xlink:href="#glyph-0-23" x="169.296875" y="457.101563"/>
<use xlink:href="#glyph-0-24" x="176.796875" y="457.101563"/>
<use xlink:href="#glyph-0-11" x="182.796875" y="457.101563"/>
<use xlink:href="#glyph-0-28" x="185.796875" y="457.101563"/>
<use xlink:href="#glyph-0-27" x="194.796875" y="457.101563"/>
<use xlink:href="#glyph-0-15" x="202.296875" y="457.101563"/>
<use xlink:href="#glyph-0-13" x="209.796875" y="457.101563"/>
<use xlink:href="#glyph-0-10" x="216.546875" y="457.101563"/>
<use xlink:href="#glyph-0-8" x="221.046875" y="457.101563"/>
<use xlink:href="#glyph-0-26" x="227.796875" y="457.101563"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 147.796875 -431.695312 C 147.796875 -423.984375 147.796875 -414.710938 147.796875 -406.113281 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 151.296875 -406.105469 L 147.796875 -396.105469 L 144.296875 -406.105469 Z M 151.296875 -406.105469 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 343.675781 -162 C 343.675781 -152.058594 320 -144 290.796875 -144 C 261.589844 -144 237.914062 -152.058594 237.914062 -162 C 237.914062 -171.941406 261.589844 -180 290.796875 -180 C 320 -180 343.675781 -171.941406 343.675781 -162 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-12" x="259.796875" y="673.101562"/>
<use xlink:href="#glyph-0-8" x="271.046875" y="673.101562"/>
<use xlink:href="#glyph-0-9" x="277.796875" y="673.101562"/>
<use xlink:href="#glyph-0-23" x="282.296875" y="673.101562"/>
<use xlink:href="#glyph-0-8" x="289.796875" y="673.101562"/>
<use xlink:href="#glyph-0-11" x="296.546875" y="673.101562"/>
<use xlink:href="#glyph-0-29" x="299.546875" y="673.101562"/>
<use xlink:href="#glyph-0-13" x="305.546875" y="673.101562"/>
<use xlink:href="#glyph-0-21" x="312.296875" y="673.101562"/>
<use xlink:href="#glyph-0-10" x="318.296875" y="673.101562"/>
<use xlink:href="#glyph-0-26" x="322.796875" y="673.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 291.546875 -215.695312 C 291.4375 -207.984375 291.304688 -198.710938 291.183594 -190.113281 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 294.683594 -190.054688 L 291.039062 -180.105469 L 287.683594 -190.152344 Z M 294.683594 -190.054688 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 247.160156 -306 C 247.160156 -296.058594 208.492188 -288 160.796875 -288 C 113.097656 -288 74.429688 -296.058594 74.429688 -306 C 74.429688 -315.941406 113.097656 -324 160.796875 -324 C 208.492188 -324 247.160156 -315.941406 247.160156 -306 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-4" x="102.796875" y="529.101562"/>
<use xlink:href="#glyph-0-5" x="110.296875" y="529.101562"/>
<use xlink:href="#glyph-0-6" x="117.796875" y="529.101562"/>
<use xlink:href="#glyph-0-26" x="125.296875" y="529.101562"/>
<use xlink:href="#glyph-0-5" x="131.296875" y="529.101562"/>
<use xlink:href="#glyph-0-20" x="138.796875" y="529.101562"/>
<use xlink:href="#glyph-0-32" x="141.796875" y="529.101562"/>
<use xlink:href="#glyph-0-15" x="144.796875" y="529.101562"/>
<use xlink:href="#glyph-0-13" x="152.296875" y="529.101562"/>
<use xlink:href="#glyph-0-10" x="159.046875" y="529.101562"/>
<use xlink:href="#glyph-0-8" x="163.546875" y="529.101562"/>
<use xlink:href="#glyph-0-11" x="170.296875" y="529.101562"/>
<use xlink:href="#glyph-0-22" x="173.296875" y="529.101562"/>
<use xlink:href="#glyph-0-6" x="182.296875" y="529.101562"/>
<use xlink:href="#glyph-0-10" x="189.796875" y="529.101562"/>
<use xlink:href="#glyph-0-5" x="194.296875" y="529.101562"/>
<use xlink:href="#glyph-0-20" x="201.796875" y="529.101562"/>
<use xlink:href="#glyph-0-5" x="204.796875" y="529.101562"/>
<use xlink:href="#glyph-0-23" x="212.296875" y="529.101562"/>
<use xlink:href="#glyph-0-24" x="219.796875" y="529.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 151.007812 -359.695312 C 152.441406 -351.984375 154.164062 -342.710938 155.761719 -334.113281 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 159.234375 -334.574219 L 157.617188 -324.105469 L 152.351562 -333.296875 Z M 159.234375 -334.574219 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-dasharray="6" stroke-miterlimit="10" d="M 190.847656 -288.941406 C 209.976562 -278.71875 234.875 -265.414062 255.160156 -254.578125 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 256.945312 -257.589844 L 264.117188 -249.789062 L 253.648438 -251.417969 Z M 256.945312 -257.589844 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(66.27451%, 80%, 66.27451%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 264.089844 -90 C 264.089844 -80.058594 246.496094 -72 224.796875 -72 C 203.09375 -72 185.5 -80.058594 185.5 -90 C 185.5 -99.941406 203.09375 -108 224.796875 -108 C 246.496094 -108 264.089844 -99.941406 264.089844 -90 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-0" x="204.796875" y="745.101562"/>
<use xlink:href="#glyph-0-8" x="211.546875" y="745.101562"/>
<use xlink:href="#glyph-0-9" x="218.296875" y="745.101562"/>
<use xlink:href="#glyph-0-32" x="223.546875" y="745.101562"/>
<use xlink:href="#glyph-0-13" x="226.546875" y="745.101562"/>
<use xlink:href="#glyph-0-20" x="233.296875" y="745.101562"/>
<use xlink:href="#glyph-0-32" x="236.296875" y="745.101562"/>
<use xlink:href="#glyph-0-33" x="239.296875" y="745.101562"/>
<use xlink:href="#glyph-0-8" x="245.296875" y="745.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-dasharray="6" stroke-miterlimit="10" d="M 165.925781 -287.847656 C 177.046875 -250.660156 203.449219 -162.375 216.789062 -117.773438 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 220.21875 -118.519531 L 219.730469 -107.933594 L 213.511719 -116.511719 Z M 220.21875 -118.519531 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(100%, 85.882353%, 60%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 251.796875 -18 C 251.796875 -8.058594 239.707031 0 224.796875 0 C 209.882812 0 197.796875 -8.058594 197.796875 -18 C 197.796875 -27.941406 209.882812 -36 224.796875 -36 C 239.707031 -36 251.796875 -27.941406 251.796875 -18 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<g fill="rgb(0%, 0%, 0%)" fill-opacity="1">
<use xlink:href="#glyph-0-34" x="216.296875" y="817.101562"/>
<use xlink:href="#glyph-0-30" x="223.046875" y="817.101562"/>
<use xlink:href="#glyph-0-35" x="232.046875" y="817.101562"/>
</g>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 224.796875 -71.695312 C 224.796875 -63.984375 224.796875 -54.710938 224.796875 -46.113281 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 228.296875 -46.105469 L 224.796875 -36.105469 L 221.296875 -46.105469 Z M 228.296875 -46.105469 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 275.488281 -144.765625 C 266.992188 -135.753906 256.273438 -124.386719 246.882812 -114.425781 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
<path fill-rule="nonzero" fill="rgb(0%, 0%, 0%)" fill-opacity="1" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke="rgb(0%, 0%, 0%)" stroke-opacity="1" stroke-miterlimit="10" d="M 249.425781 -112.023438 L 240.019531 -107.148438 L 244.332031 -116.824219 Z M 249.425781 -112.023438 " transform="matrix(1, 0, 0, 1, 4, 832)"/>
</svg>

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">
<defs>
<style>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@700');
.icon-stroke { stroke: #BE5103; stroke-width: 4; fill: none; }
.text { font: bold 44px 'Open Sans', sans-serif; fill: #293241; }
</style>
</defs>
<g transform="translate(0 0)">
<circle cx="65" cy="32" r="15" fill="none" stroke-width="4" stroke="#8b1f6b" />
<circle cx="22" cy="44" r="8" fill="none" stroke-width="4" stroke="#8b1f6b" />
<circle cx="63" cy="73" r="8" fill="none" stroke-width="4" stroke="#8b1f6b" />
<line x1="30" y1="39" x2="49" y2="33" stroke-width="3" stroke="#8b1f6b" stroke-linecap="round" />
<line x1="31" y1="45" x2="51" y2="39" stroke-width="3" stroke="#8b1f6b" stroke-linecap="round" />
<line x1="64" y1="48" x2="63" y2="64" stroke-width="4" stroke="#8b1f6b" stroke-linecap="round" />
</g>
<!-- <text x="85" y="60" class="text">GraphCast</text>-->
</svg>

After

Width:  |  Height:  |  Size: 991 B

View File

@@ -0,0 +1,81 @@
# Contributing to Suthing
We welcome contributions to Suthing! This document provides guidelines and instructions for contributing to the project.
## Getting Started
1. Fork the repository on GitHub
2. Clone your fork locally
3. Install the development dependencies:
```bash
uv sync --all-groups --extra doc-processing
```
4. Install pre-commit hooks:
```bash
pre-commit install
```
## Development Workflow
1. Create a new branch for your feature or bugfix:
```bash
git checkout -b feature/your-feature-name
```
2. Make your changes and ensure tests pass:
```bash
pytest test
```
3. Commit your changes with a descriptive message:
```bash
git commit -m "Add feature: your feature description"
```
4. Push your branch to your fork:
```bash
git push origin feature/your-feature-name
```
5. Create a Pull Request on GitHub
## Code Style
- Follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guidelines
- Use type hints for all function parameters and return values
- Write docstrings following the Google style
- Keep functions focused and small
- Add tests for new features
## Documentation
- Update relevant documentation when adding new features
- Add docstrings to all new functions and classes
- Include examples in docstrings where appropriate
- Update the changelog for significant changes
## Testing
- Write tests for all new features
- Ensure all tests pass before submitting a PR
- Add tests for bug fixes
- Maintain or improve test coverage
## Pull Request Process
1. Ensure your PR description clearly describes the problem and solution
2. Include relevant tests
3. Update documentation as needed
4. Ensure all CI checks pass
5. Request review from maintainers
## Reporting Issues
When reporting issues, please include:
- Python version
- Suthing version
- Steps to reproduce
- Expected behavior
- Actual behavior
- Any relevant error messages

View File

@@ -0,0 +1,26 @@
from pathlib import Path
import mkdocs_gen_files
nav = mkdocs_gen_files.Nav()
pname = "ontocast"
for path in sorted(Path(pname).rglob("*.py")):
module_path = path.relative_to(pname).with_suffix("")
doc_path = path.relative_to(pname).with_suffix(".md")
full_doc_path = Path("reference", doc_path)
parts = list(module_path.parts)
if parts[-1] == "__init__":
parts = parts[:-1]
if not parts:
continue
parts_str: tuple[str, ...] = tuple(parts)
nav[parts_str] = str(full_doc_path)
with mkdocs_gen_files.open(full_doc_path, "w") as f:
ident = ".".join([pname] + parts)
f.write(f"# `{ident}`\n\n::: {ident}\n")
mkdocs_gen_files.set_edit_path(full_doc_path, path)

View File

@@ -0,0 +1,28 @@
# Installation
This guide will help you install OntoCast and its dependencies.
## System Requirements
- Python 3.12 or higher
- uv (Python package installer)
## Installation Steps
```bash
uv add ontocast
```
or
```bash
pip install ontocast
```
## Next Steps
After installation, you can:
1. Read the [Quick Start](quickstart.md) guide
3. Check the [API Reference](../reference/onto.md) for detailed documentation

View File

@@ -0,0 +1,169 @@
# Quick Start
This guide will help you get started with OntoCast quickly. We'll walk through a simple example of processing a document and viewing the results.
## Prerequisites
- OntoCast installed (see [Installation](installation.md))
- A sample document to process (e.g., a pdf or a markdown file)
## Basic Example
### Query the Server
```bash
curl -X POST http://url:port/process -F "file=@sample.pdf"
curl -X POST http://url:port/process -F "file=@sample.json"
```
`url` would be `localhost` for a locally running server, default port is 8999
### Running a Server
To start an OntoCast server:
```bash
# Backend automatically detected from .env configuration
ontocast --env-path .env
# Process specific file
ontocast --env-path .env --input-path ./document.pdf
# Process with chunk limit (for testing)
ontocast --env-path .env --head-chunks 5
```
- Backend selection is **fully automatic** based on available configuration
- No explicit backend flags needed - just provide the required credentials/paths in .env
- All paths and directories are configured via .env file
### Configuration
OntoCast uses a hierarchical configuration system with environment variables. Create a `.env` file in your project directory:
```bash
# Domain configuration (used for URI generation)
CURRENT_DOMAIN=https://example.com
PORT=8999
LLM_TEMPERATURE=0.0
# LLM Configuration
LLM_PROVIDER=openai
LLM_API_KEY=your-api-key-here
LLM_MODEL_NAME=gpt-4o-mini
# Server Configuration
MAX_VISITS=3
BASE_RECURSION_LIMIT=1000
ESTIMATED_CHUNKS=30
RENDER_MODE=ontology_and_facts
ONTOLOGY_MAX_TRIPLES=50000
PARALLEL_WORKERS=4
PARALLEL_FACTS_RETRIES=3
PARALLEL_ONTOLOGY_RETRIES=3
ENABLE_ONTOLOGY_CONSOLIDATION=false
# Backend Configuration (auto-detected)
FUSEKI_URI=http://localhost:3032/test
FUSEKI_AUTH=admin:password
ONTOCAST_WORKING_DIRECTORY=/path/to/working
# Path Configuration (required for filesystem backends)
ONTOCAST_WORKING_DIRECTORY=/path/to/working/directory
ONTOCAST_ONTOLOGY_DIRECTORY=/path/to/ontology/files
ONTOCAST_CACHE_DIR=/path/to/cache/directory
# Triple Store Configuration (optional)
# For Neo4j
NEO4J_URI=bolt://localhost:7687
NEO4J_AUTH=username:password
# For Fuseki
FUSEKI_URI=http://localhost:3030
FUSEKI_AUTH=username:password
FUSEKI_DATASET=dataset_name
FUSEKI_ONTOLOGIES_DATASET=ontologies
# Optional aggregation controls
AGG_EMBEDDING_MODEL=paraphrase-multilingual-MiniLM-L12-v2
AGG_SIMILARITY_THRESHOLD=0.80
# Optional web-search grounding
WEB_SEARCH_ENABLED=false
WEB_SEARCH_PROVIDER=duckduckgo
WEB_SEARCH_TOP_K=3
```
#### Alternative: Ollama Configuration
```bash
# For Ollama
LLM_PROVIDER=ollama
LLM_BASE_URL=http://localhost:11434
LLM_MODEL_NAME=granite3.3
```
### CLI Parameters
You can use these CLI parameters:
```bash
# Use custom .env file
ontocast --env-path /path/to/custom.env
# Process specific input file
ontocast --env-path .env --input-path /path/to/document.pdf
# Process only first 5 chunks (for testing)
ontocast --env-path .env --head-chunks 5
```
**Note:** All paths and directories are configured via the `.env` file - no CLI overrides needed.
### Receive Results
After processing, the ontology and the facts graph are returned in turtle format
```json
{
"data": {
"facts": "# facts in turtle format",
"ontology": "# ontology in turtle format"
}
...
}
```
## Configuration System
OntoCast uses a hierarchical configuration system:
- **ToolConfig**: Configuration for tools (LLM, triple stores, paths)
- **ServerConfig**: Configuration for server behavior
- **Environment Variables**: Override defaults via `.env` file or environment
### Key Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `LLM_API_KEY` | API key for LLM provider | Required |
| `LLM_PROVIDER` | LLM provider (openai, ollama) | openai |
| `LLM_MODEL_NAME` | Model name | gpt-4o-mini |
| `FUSEKI_URI` + `FUSEKI_AUTH` | Use Fuseki as main triple store | Auto-detected |
| `NEO4J_URI` + `NEO4J_AUTH` | Use Neo4j as main triple store | Auto-detected |
| `ONTOCAST_WORKING_DIRECTORY` + `ONTOCAST_ONTOLOGY_DIRECTORY` | Use filesystem as main triple store | Auto-detected |
| `ONTOCAST_ONTOLOGY_DIRECTORY` | Ontology files directory | Provide seed ontologies |
| `MAX_VISITS` | Maximum visits per node | 3 |
| `BASE_RECURSION_LIMIT` | Base recursion limit for workflow | 1000 |
| `ONTOLOGY_MAX_TRIPLES` | Maximum triples allowed in ontology graph | 50000 |
| `ENABLE_ONTOLOGY_CONSOLIDATION` | Run ontology consolidation pass | false |
## Next Steps
Now that you've processed your first document, you can:
1. Try processing different types of documents (PDF, Word)
2. Configure triple stores (Neo4j, Fuseki) for persistent storage
3. Check the [API Reference](../reference/onto.md) for more details
4. Explore the [User Guide](../user_guide/concepts.md) for advanced usage

View File

@@ -0,0 +1,279 @@
# OntoCast <img src="https://raw.githubusercontent.com/growgraph/ontocast/refs/heads/main/docs/assets/favicon.ico" alt="Agentic Ontology Triplecast logo" style="height: 32px; width:32px;"/>
### Agentic ontology-assisted framework for semantic triple extraction
![Python](https://img.shields.io/badge/python-3.12-blue.svg)
[![PyPI version](https://badge.fury.io/py/ontocast.svg)](https://badge.fury.io/py/ontocast)
[![PyPI Downloads](https://static.pepy.tech/badge/ontocast)](https://pepy.tech/projects/ontocast)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![pre-commit](https://github.com/growgraph/ontocast/actions/workflows/pre-commit.yml/badge.svg)](https://github.com/growgraph/ontocast/actions/workflows/pre-commit.yml)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.17796467.svg)](https://doi.org/10.5281/zenodo.17796467)
---
## Overview
OntoCast is a framework for extracting semantic triples (creating a Knowledge Graph) from documents using an agentic, ontology-driven approach. It combines ontology management, natural language processing, and knowledge graph serialization to turn unstructured text into structured, queryable data.
---
## Key Features
- **Ontology-Guided Extraction**: Ensures semantic consistency and co-evolves ontologies
- **Entity Disambiguation**: Resolves references across document chunks
- **Multi-Format Support**: Handles text, JSON, PDF, and Markdown
- **Semantic Chunking**: Splits text based on semantic similarity
- **MCP Compatibility**: Implements Model Control Protocol endpoints
- **RDF Output**: Produces standardized RDF/Turtle
- **Triple Store Integration**: Supports Neo4j (n10s) and Apache Fuseki
- **Automatic LLM Caching**: Built-in response caching for improved performance and cost reduction
- **GraphUpdate Operations**: Token-efficient SPARQL-based updates instead of full graph regeneration
- **Budget Tracking**: Comprehensive tracking of LLM usage and triple generation metrics
- **Ontology Versioning**: Automatic semantic versioning with hash-based lineage tracking
---
## Applications
OntoCast can be used for:
- **Knowledge Graph Construction**: Build domain-specific or general-purpose knowledge graphs from documents
- **Semantic Search**: Power search and retrieval with structured triples
- **GraphRAG**: Enable retrieval-augmented generation over knowledge graphs (e.g., with LLMs)
- **Ontology Management**: Automate ontology creation, validation, and refinement
- **Data Integration**: Unify data from diverse sources into a semantic graph
---
## Installation
```sh
uv add ontocast
# or
pip install ontocast
```
---
## Configuration
## Documentation
- [Quick Start Guide](getting_started/quickstart.md) - Get started quickly
- [Configuration System](user_guide/configuration.md) - Detailed configuration guide
- [LLM Caching](user_guide/llm_caching.md) - Automatic response caching
- [Triple Store Setup](user_guide/triple_stores.md) - Triple store configuration
- [User Guide](user_guide/concepts.md) - Core concepts and workflow
- [API Reference](reference/onto.md) - Detailed API documentation
### Environment Variables
Copy the example file and edit as needed:
```bash
cp .env.example .env
# Edit with your values
```
**Main options:**
```bash
# LLM Configuration
# common
LLM_PROVIDER=openai # or ollama
LLM_MODEL_NAME=gpt-4o-mini # ollama model
LLM_TEMPERATURE=0.0
# openai
LLM_API_KEY=your_openai_api_key_here
# ollama
LLM_BASE_URL=
# Server
PORT=8999
BASE_RECURSION_LIMIT=1000
ESTIMATED_CHUNKS=30
MAX_VISITS=3
RENDER_MODE=ontology_and_facts
ONTOLOGY_MAX_TRIPLES=50000
PARALLEL_WORKERS=4
PARALLEL_FACTS_RETRIES=3
PARALLEL_ONTOLOGY_RETRIES=3
ENABLE_ONTOLOGY_CONSOLIDATION=false
# Backend Configuration (auto-detected)
FUSEKI_URI=http://localhost:3032/test
FUSEKI_AUTH=admin:password
ONTOCAST_WORKING_DIRECTORY=/path/to/working
# Optional: Triple Store Configuration (Fuseki preferred over Neo4j)
FUSEKI_URI=http://localhost:3032/test
FUSEKI_AUTH=admin/abc123-qwe
FUSEKI_DATASET=dataset_name
FUSEKI_ONTOLOGIES_DATASET=ontologies
NEO4J_URI=bolt://localhost:7689
NEO4J_AUTH=neo4j/test!passfortesting
# Aggregation controls
AGG_EMBEDDING_MODEL=paraphrase-multilingual-MiniLM-L12-v2
AGG_SIMILARITY_THRESHOLD=0.80
# Optional web grounding
WEB_SEARCH_ENABLED=false
WEB_SEARCH_PROVIDER=duckduckgo
```
---
## Triple Store Setup
OntoCast supports multiple triple store backends. When both Fuseki and Neo4j are configured, **Fuseki is preferred**.
- See [Triple Store Setup](user_guide/triple_stores.md) for detailed Docker Compose instructions and sample `.env.example` files.
- Quick summary: copy and edit the provided `.env.example` in `docker/fuseki` or `docker/neo4j`, then run `docker compose --env-file .env <service> up -d` in the respective directory.
---
## Running OntoCast Server
```bash
# Backend automatically detected from .env configuration
ontocast --env-path .env
# Process specific file
ontocast --env-path .env --input-path ./document.pdf
# Process with chunk limit (for testing)
ontocast --env-path .env --head-chunks 5
```
- Backend selection is **fully automatic** based on available configuration
- No explicit backend flags needed - just provide the required credentials/paths in .env
- All paths and directories are configured via .env file
---
## API Usage
- **POST /process**: Accepts `application/json` or file uploads (`multipart/form-data`).
- Returns: JSON with extracted facts (Turtle), ontology (Turtle), and processing metadata. Triples are also serialized to the configured triple store.
**Example:**
```bash
curl -X POST http://localhost:8999/process \
-H "Content-Type: application/json" \
-d '{"text": "Your document text here"}'
# Process a PDF file
curl -X POST http://url:port/process -F "file=@data/pdf/sample.pdf"
# Process a json file
curl -X POST http://url:port/process -F "file=@test2/sample.json"
```
---
## MCP Endpoints
- `GET /health`: Health check
- `GET /info`: Service info
- `POST /process`: Document processing
- `POST /flush`: Flush/clean triple store data (optional `dataset` query parameter for Fuseki)
---
## Filesystem Mode
If no triple store is configured, OntoCast stores ontologies and facts as Turtle files in the working directory.
---
## Notes
- JSON documents must contain a `text` field, e.g.:
```json
{ "text": "abc" }
```
- `recursion_limit` is calculated as `max_visits * estimated_chunks` (default 30, or set via `.env`)
- Default port: 8999
---
## Docker
To build the OntoCast Docker image:
```sh
docker buildx build -t growgraph/ontocast:0.1.4 . 2>&1 | tee build.log
```
---
## Project Structure
```
ontocast/
├── agent/ # Agent workflow and orchestration
├── cli/ # CLI utilities and server
├── prompt/ # LLM prompt templates
├── stategraph/ # State graph logic
├── tool/ # Triple store, chunking, and ontology tools
├── toolbox.py # Toolbox for agent tools
├── onto.py # Ontology and RDF graph handling
├── util.py # Utilities
```
Other directories:
- `docker/` Docker Compose and .env.example files for triple stores
- `data/` Example data, ontologies, and test files
- `docs/` Documentation and user guides
- `test/` Test suite
---
## Workflow
The extraction follows a multi-stage workflow:
<img src="https://github.com/growgraph/ontocast/blob/main/docs/assets/graph.png?raw=true" alt="Workflow diagram" width="350" style="float: right; margin-left: 20px;"/>
1. **Document Preparation**
- [Optional] Convert to Markdown
- Text chunking
2. **Ontology Processing**
- Ontology selection
- Text to ontology triples
- Ontology critique
3. **Fact Extraction**
- Text to facts
- Facts critique
- Ontology sublimation
4. **Chunk Normalization**
- Chunk KG aggregation
- Entity/Property Disambiguation
5. **Storage**
- Triple/KG serialization
---
## Roadmap
- [x] Add Jena Fuseki triple store for triple serialization
- [x] Add Neo4j n10s for triple serialization
- [ ] Replace triple prompting with a tool for local graph retrieval
---
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
---
## Acknowledgments
- Uses RDFlib for semantic triple management
- Uses docling for pdf/pptx conversion
- Uses OpenAI language models / open models served via Ollama for fact extraction
- Uses langchain/langgraph

View File

@@ -0,0 +1,3 @@
# `ontocast.agent`
::: ontocast.agent

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.chunk_text`
::: ontocast.agent.chunk_text

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.common`
::: ontocast.agent.common

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.convert_document`
::: ontocast.agent.convert_document

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.criticise_facts`
::: ontocast.agent.criticise_facts

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.criticise_ontology`
::: ontocast.agent.criticise_ontology

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.external_evidence`
::: ontocast.agent.external_evidence

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.normalize_ontology`
::: ontocast.agent.normalize_ontology

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.render_facts`
::: ontocast.agent.render_facts

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.render_ontology`
::: ontocast.agent.render_ontology

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.select_ontology`
::: ontocast.agent.select_ontology

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.serialize`
::: ontocast.agent.serialize

View File

@@ -0,0 +1,3 @@
# `ontocast.agent.sublimate_ontology`
::: ontocast.agent.sublimate_ontology

View File

@@ -0,0 +1,3 @@
# `ontocast.cli`
::: ontocast.cli

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.batch_process`
::: ontocast.cli.batch_process

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.cmp_states`
::: ontocast.cli.cmp_states

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.merge_ontologies`
::: ontocast.cli.merge_ontologies

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.pdfs_to_markdown`
::: ontocast.cli.pdfs_to_markdown

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.plot_graph`
::: ontocast.cli.plot_graph

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.serve`
::: ontocast.cli.serve

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.split_chunks`
::: ontocast.cli.split_chunks

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.test_api`
::: ontocast.cli.test_api

View File

@@ -0,0 +1,3 @@
# `ontocast.cli.util`
::: ontocast.cli.util

View File

@@ -0,0 +1,3 @@
# `ontocast.config`
::: ontocast.config

View File

@@ -0,0 +1,3 @@
# `ontocast.onto`
::: ontocast.onto

View File

@@ -0,0 +1,3 @@
# `ontocast.onto`
::: ontocast.onto

View File

@@ -0,0 +1,3 @@
# `ontocast.onto.constants`
::: ontocast.onto.constants

View File

@@ -0,0 +1,3 @@
# `ontocast.onto.content_unit`
::: ontocast.onto.content_unit

View File

@@ -0,0 +1,3 @@
# `ontocast.onto.context`
::: ontocast.onto.context

Some files were not shown because too many files have changed in this diff Show More