Merge branch 'dev' into fix_issue_with_base_config
This commit is contained in:
commit
0cb334c9ae
15 changed files with 4398 additions and 4225 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from functools import lru_cache
|
||||
from cognee.root_dir import get_absolute_path, ensure_absolute_path
|
||||
|
|
@ -11,7 +12,9 @@ class BaseConfig(BaseSettings):
|
|||
data_root_directory: str = get_absolute_path(".data_storage")
|
||||
system_root_directory: str = get_absolute_path(".cognee_system")
|
||||
cache_root_directory: str = get_absolute_path(".cognee_cache")
|
||||
logs_root_directory: str = os.getenv("COGNEE_LOGS_DIR", "/tmp/cognee_logs")
|
||||
logs_root_directory: str = os.getenv(
|
||||
"COGNEE_LOGS_DIR", str(os.path.join(os.path.dirname(os.path.dirname(__file__)), "logs"))
|
||||
)
|
||||
monitoring_tool: object = Observer.NONE
|
||||
|
||||
@pydantic.model_validator(mode="after")
|
||||
|
|
@ -31,14 +34,8 @@ class BaseConfig(BaseSettings):
|
|||
# Require absolute paths for root directories
|
||||
self.data_root_directory = ensure_absolute_path(self.data_root_directory)
|
||||
self.system_root_directory = ensure_absolute_path(self.system_root_directory)
|
||||
# logs_root_directory may be outside project root; keep as-is if absolute or make absolute if relative
|
||||
try:
|
||||
if not os.path.isabs(self.logs_root_directory):
|
||||
# If relative, place under current working directory
|
||||
self.logs_root_directory = os.path.abspath(self.logs_root_directory)
|
||||
except Exception:
|
||||
# If anything goes wrong, fall back to /tmp/cognee_logs
|
||||
self.logs_root_directory = "/tmp/cognee_logs"
|
||||
self.logs_root_directory = ensure_absolute_path(self.logs_root_directory)
|
||||
|
||||
# Set monitoring tool based on available keys
|
||||
if self.langfuse_public_key and self.langfuse_secret_key:
|
||||
self.monitoring_tool = Observer.LANGFUSE
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
For the purposes of identifying timestamps in a query, you are tasked with extracting relevant timestamps from the query.
|
||||
## Timestamp requirements
|
||||
- If the query contains interval extrack both starts_at and ends_at properties
|
||||
- If the query contains an instantaneous timestamp, starts_at and ends_at should be the same
|
||||
- If the query its open-ended (before 2009 or after 2009), the corresponding non defined end of the time should be none
|
||||
-For example: "before 2009" -- starts_at: None, ends_at: 2009 or "after 2009" -- starts_at: 2009, ends_at: None
|
||||
- Put always the data that comes first in time as starts_at and the timestamps that comes second in time as ends_at
|
||||
- If starts_at or ends_at cannot be extracted both of them has to be None
|
||||
## Output Format
|
||||
Your reply should be a JSON: list of dictionaries with the following structure:
|
||||
```python
|
||||
class QueryInterval(BaseModel):
|
||||
starts_at: Optional[Timestamp] = None
|
||||
ends_at: Optional[Timestamp] = None
|
||||
```
|
||||
You are tasked with identifying relevant time periods where the answer to a given query should be searched.
|
||||
Current date is: `{{ time_now }}`. Determine relevant period(s) and return structured intervals.
|
||||
|
||||
Extraction rules:
|
||||
|
||||
1. Query without specific timestamp: use the time period with starts_at set to None and ends_at set to now.
|
||||
2. Explicit time intervals: If the query specifies a range (e.g., from 2010 to 2020, between January and March 2023), extract both start and end dates. Always assign the earlier date to starts_at and the later date to ends_at.
|
||||
3. Single timestamp: If the query refers to one specific moment (e.g., in 2015, on March 5, 2022), set starts_at and ends_at to that same timestamp.
|
||||
4. Open-ended time references: For phrases such as "before X" or "after X", represent the unspecified side as None. For example: before 2009 → starts_at: None, ends_at: 2009; after 2009 → starts_at: 2009, ends_at: None.
|
||||
5. Current-time references ("now", "current", "today"): If the query explicitly refers to the present, set both starts_at and ends_at to now (the ingestion timestamp).
|
||||
6. "Who is" and "Who was" questions: These imply a general identity or biographical inquiry without a specific temporal scope. Set both starts_at and ends_at to None.
|
||||
7. Ordering rule: Always ensure the earlier date is assigned to starts_at and the later date to ends_at.
|
||||
8. No temporal information: If no valid or inferable time reference is found, set both starts_at and ends_at to None.
|
||||
|
|
@ -21,7 +21,8 @@ def get_ontology_resolver_from_env(
|
|||
Supported value: "rdflib".
|
||||
matching_strategy (str): The matching strategy to apply.
|
||||
Supported value: "fuzzy".
|
||||
ontology_file_path (str): Path to the ontology file required for the resolver.
|
||||
ontology_file_path (str): Path to the ontology file(s) required for the resolver.
|
||||
Can be a single path or comma-separated paths for multiple files.
|
||||
|
||||
Returns:
|
||||
BaseOntologyResolver: An instance of the requested ontology resolver.
|
||||
|
|
@ -31,8 +32,13 @@ def get_ontology_resolver_from_env(
|
|||
or if required parameters are missing.
|
||||
"""
|
||||
if ontology_resolver == "rdflib" and matching_strategy == "fuzzy" and ontology_file_path:
|
||||
if "," in ontology_file_path:
|
||||
file_paths = [path.strip() for path in ontology_file_path.split(",")]
|
||||
else:
|
||||
file_paths = ontology_file_path
|
||||
|
||||
return RDFLibOntologyResolver(
|
||||
matching_strategy=FuzzyMatchingStrategy(), ontology_file=ontology_file_path
|
||||
matching_strategy=FuzzyMatchingStrategy(), ontology_file=file_paths
|
||||
)
|
||||
else:
|
||||
raise EnvironmentError(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import difflib
|
||||
from cognee.shared.logging_utils import get_logger
|
||||
from collections import deque
|
||||
from typing import List, Tuple, Dict, Optional, Any
|
||||
from typing import List, Tuple, Dict, Optional, Any, Union
|
||||
from rdflib import Graph, URIRef, RDF, RDFS, OWL
|
||||
|
||||
from cognee.modules.ontology.exceptions import (
|
||||
|
|
@ -26,22 +26,50 @@ class RDFLibOntologyResolver(BaseOntologyResolver):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
ontology_file: Optional[str] = None,
|
||||
ontology_file: Optional[Union[str, List[str]]] = None,
|
||||
matching_strategy: Optional[MatchingStrategy] = None,
|
||||
) -> None:
|
||||
super().__init__(matching_strategy)
|
||||
self.ontology_file = ontology_file
|
||||
try:
|
||||
if ontology_file and os.path.exists(ontology_file):
|
||||
files_to_load = []
|
||||
if ontology_file is not None:
|
||||
if isinstance(ontology_file, str):
|
||||
files_to_load = [ontology_file]
|
||||
elif isinstance(ontology_file, list):
|
||||
files_to_load = ontology_file
|
||||
else:
|
||||
raise ValueError(
|
||||
f"ontology_file must be a string, list of strings, or None. Got: {type(ontology_file)}"
|
||||
)
|
||||
|
||||
if files_to_load:
|
||||
self.graph = Graph()
|
||||
self.graph.parse(ontology_file)
|
||||
logger.info("Ontology loaded successfully from file: %s", ontology_file)
|
||||
loaded_files = []
|
||||
for file_path in files_to_load:
|
||||
if os.path.exists(file_path):
|
||||
self.graph.parse(file_path)
|
||||
loaded_files.append(file_path)
|
||||
logger.info("Ontology loaded successfully from file: %s", file_path)
|
||||
else:
|
||||
logger.warning(
|
||||
"Ontology file '%s' not found. Skipping this file.",
|
||||
file_path,
|
||||
)
|
||||
|
||||
if not loaded_files:
|
||||
logger.info(
|
||||
"No valid ontology files found. No owl ontology will be attached to the graph."
|
||||
)
|
||||
self.graph = None
|
||||
else:
|
||||
logger.info("Total ontology files loaded: %d", len(loaded_files))
|
||||
else:
|
||||
logger.info(
|
||||
"Ontology file '%s' not found. No owl ontology will be attached to the graph.",
|
||||
ontology_file,
|
||||
"No ontology file provided. No owl ontology will be attached to the graph."
|
||||
)
|
||||
self.graph = None
|
||||
|
||||
self.build_lookup()
|
||||
except Exception as e:
|
||||
logger.error("Failed to load ontology", exc_info=e)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ async def handle_task(
|
|||
additional_properties={
|
||||
"task_name": running_task.executable.__name__,
|
||||
"cognee_version": cognee_version,
|
||||
"tenant_id": str(user.tenant_id) if user.tenant_id else "Single User Tenant",
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -49,6 +50,7 @@ async def handle_task(
|
|||
additional_properties={
|
||||
"task_name": running_task.executable.__name__,
|
||||
"cognee_version": cognee_version,
|
||||
"tenant_id": str(user.tenant_id) if user.tenant_id else "Single User Tenant",
|
||||
},
|
||||
)
|
||||
except Exception as error:
|
||||
|
|
@ -62,6 +64,7 @@ async def handle_task(
|
|||
additional_properties={
|
||||
"task_name": running_task.executable.__name__,
|
||||
"cognee_version": cognee_version,
|
||||
"tenant_id": str(user.tenant_id) if user.tenant_id else "Single User Tenant",
|
||||
},
|
||||
)
|
||||
raise error
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ async def run_tasks_with_telemetry(
|
|||
additional_properties={
|
||||
"pipeline_name": str(pipeline_name),
|
||||
"cognee_version": cognee_version,
|
||||
"tenant_id": str(user.tenant_id) if user.tenant_id else "Single User Tenant",
|
||||
}
|
||||
| config,
|
||||
)
|
||||
|
|
@ -42,6 +43,7 @@ async def run_tasks_with_telemetry(
|
|||
additional_properties={
|
||||
"pipeline_name": str(pipeline_name),
|
||||
"cognee_version": cognee_version,
|
||||
"tenant_id": str(user.tenant_id) if user.tenant_id else "Single User Tenant",
|
||||
}
|
||||
| config,
|
||||
)
|
||||
|
|
@ -58,6 +60,7 @@ async def run_tasks_with_telemetry(
|
|||
additional_properties={
|
||||
"pipeline_name": str(pipeline_name),
|
||||
"cognee_version": cognee_version,
|
||||
"tenant_id": str(user.tenant_id) if user.tenant_id else "Single User Tenant",
|
||||
}
|
||||
| config,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
import asyncio
|
||||
from typing import Any, Optional, List, Type
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from operator import itemgetter
|
||||
from cognee.infrastructure.databases.vector import get_vector_engine
|
||||
|
|
@ -79,7 +79,11 @@ class TemporalRetriever(GraphCompletionRetriever):
|
|||
else:
|
||||
base_directory = None
|
||||
|
||||
system_prompt = render_prompt(prompt_path, {}, base_directory=base_directory)
|
||||
time_now = datetime.now().strftime("%d-%m-%Y")
|
||||
|
||||
system_prompt = render_prompt(
|
||||
prompt_path, {"time_now": time_now}, base_directory=base_directory
|
||||
)
|
||||
|
||||
interval = await LLMGateway.acreate_structured_output(query, system_prompt, QueryInterval)
|
||||
|
||||
|
|
@ -108,8 +112,6 @@ class TemporalRetriever(GraphCompletionRetriever):
|
|||
|
||||
graph_engine = await get_graph_engine()
|
||||
|
||||
triplets = []
|
||||
|
||||
if time_from and time_to:
|
||||
ids = await graph_engine.collect_time_ids(time_from=time_from, time_to=time_to)
|
||||
elif time_from:
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ async def search(
|
|||
send_telemetry(
|
||||
"cognee.search EXECUTION STARTED",
|
||||
user.id,
|
||||
additional_properties={"cognee_version": cognee_version},
|
||||
additional_properties={
|
||||
"cognee_version": cognee_version,
|
||||
"tenant_id": str(user.tenant_id) if user.tenant_id else "Single User Tenant",
|
||||
},
|
||||
)
|
||||
|
||||
# Use search function filtered by permissions if access control is enabled
|
||||
|
|
@ -108,7 +111,10 @@ async def search(
|
|||
send_telemetry(
|
||||
"cognee.search EXECUTION COMPLETED",
|
||||
user.id,
|
||||
additional_properties={"cognee_version": cognee_version},
|
||||
additional_properties={
|
||||
"cognee_version": cognee_version,
|
||||
"tenant_id": str(user.tenant_id) if user.tenant_id else "Single User Tenant",
|
||||
},
|
||||
)
|
||||
|
||||
await log_result(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
import sys
|
||||
import logging
|
||||
import tempfile
|
||||
import structlog
|
||||
import traceback
|
||||
import platform
|
||||
|
|
@ -76,12 +77,8 @@ log_levels = {
|
|||
# Track if structlog logging has been configured
|
||||
_is_structlog_configured = False
|
||||
|
||||
# Logging directory resolution
|
||||
# Default writable location for most Unix-based systems
|
||||
DEFAULT_LOGS_DIR = "/tmp/cognee_logs"
|
||||
|
||||
|
||||
def _resolve_logs_dir():
|
||||
def resolve_logs_dir():
|
||||
"""Resolve a writable logs directory.
|
||||
|
||||
Priority:
|
||||
|
|
@ -93,20 +90,13 @@ def _resolve_logs_dir():
|
|||
"""
|
||||
candidate_paths = []
|
||||
|
||||
# Prefer configuration from BaseConfig
|
||||
try:
|
||||
from cognee.base_config import get_base_config
|
||||
from cognee.base_config import get_base_config
|
||||
|
||||
base_config = get_base_config()
|
||||
if getattr(base_config, "logs_root_directory", None):
|
||||
candidate_paths.append(Path(base_config.logs_root_directory))
|
||||
except Exception:
|
||||
# If base config is unavailable during early imports, fall back to env
|
||||
env_dir = os.environ.get("COGNEE_LOGS_DIR")
|
||||
if env_dir:
|
||||
candidate_paths.append(Path(env_dir))
|
||||
candidate_paths.append(Path(DEFAULT_LOGS_DIR))
|
||||
candidate_paths.append(Path.cwd() / "logs")
|
||||
base_config = get_base_config()
|
||||
candidate_paths.append(Path(base_config.logs_root_directory))
|
||||
|
||||
tmp_candidate_path = os.path.join(tempfile.gettempdir(), "cognee_logs")
|
||||
candidate_paths.append(tmp_candidate_path)
|
||||
|
||||
for candidate in candidate_paths:
|
||||
try:
|
||||
|
|
@ -470,8 +460,17 @@ def setup_logging(log_level=None, name=None):
|
|||
stream_handler.setFormatter(console_formatter)
|
||||
stream_handler.setLevel(log_level)
|
||||
|
||||
root_logger = logging.getLogger()
|
||||
if root_logger.hasHandlers():
|
||||
root_logger.handlers.clear()
|
||||
root_logger.addHandler(stream_handler)
|
||||
|
||||
# Note: root logger needs to be set at NOTSET to allow all messages through and specific stream and file handlers
|
||||
# can define their own levels.
|
||||
root_logger.setLevel(logging.NOTSET)
|
||||
|
||||
# Resolve logs directory with env and safe fallbacks
|
||||
logs_dir = _resolve_logs_dir()
|
||||
logs_dir = resolve_logs_dir()
|
||||
|
||||
# Check if we already have a log file path from the environment
|
||||
# NOTE: environment variable must be used here as it allows us to
|
||||
|
|
@ -484,24 +483,15 @@ def setup_logging(log_level=None, name=None):
|
|||
log_file_path = str((logs_dir / f"{start_time}.log").resolve())
|
||||
os.environ["LOG_FILE_NAME"] = log_file_path
|
||||
|
||||
# Create a file handler that uses our custom PlainFileHandler if possible
|
||||
file_handler = None
|
||||
if log_file_path:
|
||||
try:
|
||||
file_handler = PlainFileHandler(log_file_path, encoding="utf-8")
|
||||
file_handler.setLevel(DEBUG)
|
||||
except Exception:
|
||||
# If file handler cannot be created, fall back to console-only logging
|
||||
file_handler = None
|
||||
|
||||
# Configure root logger
|
||||
root_logger = logging.getLogger()
|
||||
if root_logger.hasHandlers():
|
||||
root_logger.handlers.clear()
|
||||
root_logger.addHandler(stream_handler)
|
||||
if file_handler is not None:
|
||||
try:
|
||||
# Create a file handler that uses our custom PlainFileHandler
|
||||
file_handler = PlainFileHandler(log_file_path, encoding="utf-8")
|
||||
file_handler.setLevel(DEBUG)
|
||||
root_logger.addHandler(file_handler)
|
||||
root_logger.setLevel(log_level)
|
||||
except Exception as e:
|
||||
# Note: Exceptions happen in case of read only file systems or log file path poiting to location where it does
|
||||
# not have write permission. Logging to file is not mandatory so we just log a warning to console.
|
||||
root_logger.warning(f"Warning: Could not create log file handler at {log_file_path}: {e}")
|
||||
|
||||
if log_level > logging.DEBUG:
|
||||
import warnings
|
||||
|
|
@ -541,6 +531,10 @@ def setup_logging(log_level=None, name=None):
|
|||
|
||||
# Get a configured logger and log system information
|
||||
logger = structlog.get_logger(name if name else __name__)
|
||||
|
||||
if logs_dir is not None:
|
||||
logger.info(f"Log file created at: {log_file_path}", log_file=log_file_path)
|
||||
|
||||
# Detailed initialization for regular usage
|
||||
logger.info(
|
||||
"Logging initialized",
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ async def complete_database_ingestion(schema, migrate_column_data):
|
|||
id=uuid5(NAMESPACE_OID, name=column_node_id),
|
||||
name=column_node_id,
|
||||
properties=f"{key} {value} {table_name}",
|
||||
description=f"column from relational database table={table_name}. Column name={key} and value={value}. The value of the column is related to the following row with this id: {row_node.id}. This column has the following ID: {column_node_id}",
|
||||
description=f"column from relational database table={table_name}. Column name={key} and value={value}. This column has the following ID: {column_node_id}",
|
||||
)
|
||||
node_mapping[column_node_id] = column_node
|
||||
|
||||
|
|
|
|||
|
|
@ -489,3 +489,154 @@ def test_get_ontology_resolver_from_env_resolver_functionality():
|
|||
assert nodes == []
|
||||
assert relationships == []
|
||||
assert start_node is None
|
||||
|
||||
|
||||
def test_multifile_ontology_loading_success():
|
||||
"""Test successful loading of multiple ontology files."""
|
||||
ns1 = Namespace("http://example.org/cars#")
|
||||
ns2 = Namespace("http://example.org/tech#")
|
||||
|
||||
g1 = Graph()
|
||||
g1.add((ns1.Vehicle, RDF.type, OWL.Class))
|
||||
g1.add((ns1.Car, RDF.type, OWL.Class))
|
||||
g1.add((ns1.Car, RDFS.subClassOf, ns1.Vehicle))
|
||||
g1.add((ns1.Audi, RDF.type, ns1.Car))
|
||||
g1.add((ns1.BMW, RDF.type, ns1.Car))
|
||||
|
||||
g2 = Graph()
|
||||
g2.add((ns2.Company, RDF.type, OWL.Class))
|
||||
g2.add((ns2.TechCompany, RDF.type, OWL.Class))
|
||||
g2.add((ns2.TechCompany, RDFS.subClassOf, ns2.Company))
|
||||
g2.add((ns2.Apple, RDF.type, ns2.TechCompany))
|
||||
g2.add((ns2.Google, RDF.type, ns2.TechCompany))
|
||||
|
||||
import tempfile
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".owl", delete=False) as f1:
|
||||
g1.serialize(f1.name, format="xml")
|
||||
file1_path = f1.name
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".owl", delete=False) as f2:
|
||||
g2.serialize(f2.name, format="xml")
|
||||
file2_path = f2.name
|
||||
|
||||
try:
|
||||
resolver = RDFLibOntologyResolver(ontology_file=[file1_path, file2_path])
|
||||
|
||||
assert resolver.graph is not None
|
||||
|
||||
assert "car" in resolver.lookup["classes"]
|
||||
assert "vehicle" in resolver.lookup["classes"]
|
||||
assert "company" in resolver.lookup["classes"]
|
||||
assert "techcompany" in resolver.lookup["classes"]
|
||||
|
||||
assert "audi" in resolver.lookup["individuals"]
|
||||
assert "bmw" in resolver.lookup["individuals"]
|
||||
assert "apple" in resolver.lookup["individuals"]
|
||||
assert "google" in resolver.lookup["individuals"]
|
||||
|
||||
car_match = resolver.find_closest_match("Audi", "individuals")
|
||||
assert car_match == "audi"
|
||||
|
||||
tech_match = resolver.find_closest_match("Google", "individuals")
|
||||
assert tech_match == "google"
|
||||
|
||||
finally:
|
||||
import os
|
||||
|
||||
os.unlink(file1_path)
|
||||
os.unlink(file2_path)
|
||||
|
||||
|
||||
def test_multifile_ontology_with_missing_files():
|
||||
"""Test loading multiple ontology files where some don't exist."""
|
||||
ns = Namespace("http://example.org/test#")
|
||||
g = Graph()
|
||||
g.add((ns.Car, RDF.type, OWL.Class))
|
||||
g.add((ns.Audi, RDF.type, ns.Car))
|
||||
|
||||
import tempfile
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".owl", delete=False) as f:
|
||||
g.serialize(f.name, format="xml")
|
||||
valid_file = f.name
|
||||
|
||||
try:
|
||||
resolver = RDFLibOntologyResolver(
|
||||
ontology_file=["nonexistent_file_1.owl", valid_file, "nonexistent_file_2.owl"]
|
||||
)
|
||||
|
||||
assert resolver.graph is not None
|
||||
|
||||
assert "car" in resolver.lookup["classes"]
|
||||
assert "audi" in resolver.lookup["individuals"]
|
||||
|
||||
match = resolver.find_closest_match("Audi", "individuals")
|
||||
assert match == "audi"
|
||||
|
||||
finally:
|
||||
import os
|
||||
|
||||
os.unlink(valid_file)
|
||||
|
||||
|
||||
def test_multifile_ontology_all_files_missing():
|
||||
"""Test loading multiple ontology files where all files are missing."""
|
||||
resolver = RDFLibOntologyResolver(
|
||||
ontology_file=["nonexistent_file_1.owl", "nonexistent_file_2.owl", "nonexistent_file_3.owl"]
|
||||
)
|
||||
|
||||
assert resolver.graph is None
|
||||
|
||||
assert resolver.lookup["classes"] == {}
|
||||
assert resolver.lookup["individuals"] == {}
|
||||
|
||||
|
||||
def test_multifile_ontology_with_overlapping_entities():
|
||||
"""Test loading multiple ontology files with overlapping/related entities."""
|
||||
ns = Namespace("http://example.org/automotive#")
|
||||
|
||||
g1 = Graph()
|
||||
g1.add((ns.Vehicle, RDF.type, OWL.Class))
|
||||
g1.add((ns.Car, RDF.type, OWL.Class))
|
||||
g1.add((ns.Car, RDFS.subClassOf, ns.Vehicle))
|
||||
|
||||
g2 = Graph()
|
||||
g2.add((ns.LuxuryCar, RDF.type, OWL.Class))
|
||||
g2.add((ns.LuxuryCar, RDFS.subClassOf, ns.Car))
|
||||
g2.add((ns.Mercedes, RDF.type, ns.LuxuryCar))
|
||||
g2.add((ns.BMW, RDF.type, ns.LuxuryCar))
|
||||
|
||||
import tempfile
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".owl", delete=False) as f1:
|
||||
g1.serialize(f1.name, format="xml")
|
||||
file1_path = f1.name
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".owl", delete=False) as f2:
|
||||
g2.serialize(f2.name, format="xml")
|
||||
file2_path = f2.name
|
||||
|
||||
try:
|
||||
resolver = RDFLibOntologyResolver(ontology_file=[file1_path, file2_path])
|
||||
|
||||
assert "vehicle" in resolver.lookup["classes"]
|
||||
assert "car" in resolver.lookup["classes"]
|
||||
assert "luxurycar" in resolver.lookup["classes"]
|
||||
|
||||
assert "mercedes" in resolver.lookup["individuals"]
|
||||
assert "bmw" in resolver.lookup["individuals"]
|
||||
|
||||
nodes, relationships, start_node = resolver.get_subgraph("Mercedes", "individuals")
|
||||
|
||||
uri_labels = {resolver._uri_to_key(n.uri) for n in nodes}
|
||||
assert "mercedes" in uri_labels
|
||||
assert "luxurycar" in uri_labels
|
||||
assert "car" in uri_labels
|
||||
assert "vehicle" in uri_labels
|
||||
|
||||
finally:
|
||||
import os
|
||||
|
||||
os.unlink(file1_path)
|
||||
os.unlink(file2_path)
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ async def main():
|
|||
"What happened between 2000 and 2006?",
|
||||
"What happened between 1903 and 1995, I am interested in the Selected Works of Arnulf Øverland Ole Peter Arnulf Øverland?",
|
||||
"Who is Attaphol Buspakom Attaphol Buspakom?",
|
||||
"Who was Arnulf Øverland?",
|
||||
]
|
||||
|
||||
for query_text in queries:
|
||||
|
|
|
|||
6
poetry.lock
generated
6
poetry.lock
generated
|
|
@ -1,4 +1,4 @@
|
|||
# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "accelerate"
|
||||
|
|
@ -2543,7 +2543,6 @@ files = [
|
|||
{file = "fastuuid-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b31dd488d0778c36f8279b306dc92a42f16904cba54acca71e107d65b60b0c"},
|
||||
{file = "fastuuid-0.12.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:b19361ee649365eefc717ec08005972d3d1eb9ee39908022d98e3bfa9da59e37"},
|
||||
{file = "fastuuid-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:8fc66b11423e6f3e1937385f655bedd67aebe56a3dcec0cb835351cfe7d358c9"},
|
||||
{file = "fastuuid-0.12.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:2925f67b88d47cb16aa3eb1ab20fdcf21b94d74490e0818c91ea41434b987493"},
|
||||
{file = "fastuuid-0.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7b15c54d300279ab20a9cc0579ada9c9f80d1bc92997fc61fb7bf3103d7cb26b"},
|
||||
{file = "fastuuid-0.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:458f1bc3ebbd76fdb89ad83e6b81ccd3b2a99fa6707cd3650b27606745cfb170"},
|
||||
{file = "fastuuid-0.12.0-cp38-cp38-manylinux_2_34_x86_64.whl", hash = "sha256:a8f0f83fbba6dc44271a11b22e15838641b8c45612cdf541b4822a5930f6893c"},
|
||||
|
|
@ -4170,8 +4169,6 @@ groups = ["main"]
|
|||
markers = "extra == \"dlt\""
|
||||
files = [
|
||||
{file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"},
|
||||
{file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"},
|
||||
{file = "jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -8593,7 +8590,6 @@ files = [
|
|||
{file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"},
|
||||
{file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"},
|
||||
{file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"},
|
||||
{file = "psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142"},
|
||||
{file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"},
|
||||
{file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"},
|
||||
{file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[project]
|
||||
name = "cognee"
|
||||
|
||||
version = "0.3.7"
|
||||
version = "0.3.7.dev1"
|
||||
description = "Cognee - is a library for enriching LLM context with a semantic layer for better understanding and reasoning."
|
||||
authors = [
|
||||
{ name = "Vasilije Markovic" },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue