diff --git a/.server-stderr.log b/.server-stderr.log index a1dfcb8..dcdfbbc 100644 --- a/.server-stderr.log +++ b/.server-stderr.log @@ -1,715 +1,4 @@ -INFO: Started server process [12700] +INFO: Started server process [38332] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -ERROR: Exception in ASGI application -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1936, in _exec_single_context - self.dialect.do_executemany( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 949, in do_executemany - cursor.executemany(statement, parameters) -sqlite3.OperationalError: database is locked - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 421, in run_asgi - result = await app( # type: ignore[func-returns-value] - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 56, in __call__ - return await self.app(scope, receive, send) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\applications.py", line 1159, in __call__ - await super().__call__(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\applications.py", line 90, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 186, in __call__ - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 164, in __call__ - await self.app(scope, receive, _send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\exceptions.py", line 63, in __call__ - await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__ - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 660, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 680, in app - await route.handle(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 276, in handle - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 134, in app - await wrap_app_handling_exceptions(app, request)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 120, in app - response = await f(request) - ^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 674, in app - raw_response = await run_endpoint_function( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 330, in run_endpoint_function - return await run_in_threadpool(dependant.call, **values) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\concurrency.py", line 32, in run_in_threadpool - return await anyio.to_thread.run_sync(func) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync - return await get_async_backend().run_sync_in_worker_thread( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 2518, in run_sync_in_worker_thread - return await future - ^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 1002, in run - result = context.run(func, *args) - ^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\api\routes.py", line 432, in knowledge_gaps - with session_scope(database_url) as session: - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\contextlib.py", line 144, in __exit__ - next(self.gen) - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\database\session.py", line 46, in session_scope - session.commit() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 2030, in commit - trans.commit(_to_root=True) - File "", line 2, in commit - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\state_changes.py", line 137, in _go - ret_value = fn(self, *arg, **kw) - ^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 1311, in commit - self._prepare_impl() - File "", line 2, in _prepare_impl - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\state_changes.py", line 137, in _go - ret_value = fn(self, *arg, **kw) - ^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 1286, in _prepare_impl - self.session.flush() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4331, in flush - self._flush(objects) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4466, in _flush - with util.safe_reraise(): - ^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\util\langhelpers.py", line 121, in __exit__ - raise exc_value.with_traceback(exc_tb) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4427, in _flush - flush_context.execute() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\unitofwork.py", line 466, in execute - rec.execute(self) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\unitofwork.py", line 642, in execute - util.preloaded.orm_persistence.save_obj( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\persistence.py", line 85, in save_obj - _emit_update_statements( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\persistence.py", line 912, in _emit_update_statements - c = connection.execute( - ^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute - return meth( - ^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\sql\elements.py", line 527, in _execute_on_connection - return connection._execute_clauseelement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement - ret = self._execute_context( - ^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context - return self._exec_single_context( - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context - self._handle_dbapi_exception( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 2363, in _handle_dbapi_exception - raise sqlalchemy_exception.with_traceback(exc_info[2]) from e - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1936, in _exec_single_context - self.dialect.do_executemany( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 949, in do_executemany - cursor.executemany(statement, parameters) -sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked -[SQL: UPDATE knowledge_gaps SET updated_at=? WHERE knowledge_gaps.id = ?] -[parameters: [('2026-05-13 03:58:35.233275', 1), ('2026-05-13 03:58:35.234725', 2), ('2026-05-13 03:58:35.238062', 3), ('2026-05-13 03:58:35.239065', 4), ('2026-05-13 03:58:35.240068', 5), ('2026-05-13 03:58:35.240068', 6), ('2026-05-13 03:58:35.241077', 7), ('2026-05-13 03:58:35.245134', 8) ... displaying 10 of 32 total bound parameter sets ... ('2026-05-13 03:58:35.287923', 31), ('2026-05-13 03:58:35.287923', 32)]] -(Background on this error at: https://sqlalche.me/e/20/e3q8) -ERROR: Exception in ASGI application -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1936, in _exec_single_context - self.dialect.do_executemany( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 949, in do_executemany - cursor.executemany(statement, parameters) -sqlite3.OperationalError: database is locked - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 421, in run_asgi - result = await app( # type: ignore[func-returns-value] - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 56, in __call__ - return await self.app(scope, receive, send) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\applications.py", line 1159, in __call__ - await super().__call__(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\applications.py", line 90, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 186, in __call__ - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 164, in __call__ - await self.app(scope, receive, _send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\exceptions.py", line 63, in __call__ - await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__ - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 660, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 680, in app - await route.handle(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 276, in handle - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 134, in app - await wrap_app_handling_exceptions(app, request)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 120, in app - response = await f(request) - ^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 674, in app - raw_response = await run_endpoint_function( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 330, in run_endpoint_function - return await run_in_threadpool(dependant.call, **values) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\concurrency.py", line 32, in run_in_threadpool - return await anyio.to_thread.run_sync(func) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync - return await get_async_backend().run_sync_in_worker_thread( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 2518, in run_sync_in_worker_thread - return await future - ^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 1002, in run - result = context.run(func, *args) - ^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\api\routes.py", line 434, in knowledge_gaps - return KnowledgeGapDetector(session).list_open(project.id, max(min(limit, 300), 1)) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 27, in list_open - self.detect(project_id, limit=limit) - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 22, in detect - gaps.extend(self._underconnected_entity_gaps(project_id)) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 119, in _underconnected_entity_gaps - self._upsert_gap( - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 188, in _upsert_gap - self.session.flush() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4331, in flush - self._flush(objects) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4466, in _flush - with util.safe_reraise(): - ^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\util\langhelpers.py", line 121, in __exit__ - raise exc_value.with_traceback(exc_tb) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4427, in _flush - flush_context.execute() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\unitofwork.py", line 466, in execute - rec.execute(self) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\unitofwork.py", line 642, in execute - util.preloaded.orm_persistence.save_obj( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\persistence.py", line 85, in save_obj - _emit_update_statements( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\persistence.py", line 912, in _emit_update_statements - c = connection.execute( - ^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute - return meth( - ^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\sql\elements.py", line 527, in _execute_on_connection - return connection._execute_clauseelement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement - ret = self._execute_context( - ^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context - return self._exec_single_context( - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context - self._handle_dbapi_exception( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 2363, in _handle_dbapi_exception - raise sqlalchemy_exception.with_traceback(exc_info[2]) from e - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1936, in _exec_single_context - self.dialect.do_executemany( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 949, in do_executemany - cursor.executemany(statement, parameters) -sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked -[SQL: UPDATE knowledge_gaps SET updated_at=? WHERE knowledge_gaps.id = ?] -[parameters: [('2026-05-13 03:59:40.992293', 1), ('2026-05-13 03:59:40.992293', 2), ('2026-05-13 03:59:40.992293', 4), ('2026-05-13 03:59:40.993551', 5), ('2026-05-13 03:59:40.994556', 6), ('2026-05-13 03:59:40.995558', 7), ('2026-05-13 03:59:40.995558', 8), ('2026-05-13 03:59:40.997570', 9) ... displaying 10 of 30 total bound parameter sets ... ('2026-05-13 03:59:41.015370', 31), ('2026-05-13 03:59:41.015370', 32)]] -(Background on this error at: https://sqlalche.me/e/20/e3q8) -C:\Users\lasta\MyProject\AI\crawler_platform\app\core\crawler\site_crawler.py:409: SAWarning: Identity map already had an identity for (, (1,), None), replacing it with newly flushed object. Are there load operations occurring inside of an event handler within the flush? - self.repository.session.flush() -ERROR: Exception in ASGI application -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context - self.dialect.do_execute( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 952, in do_execute - cursor.execute(statement, parameters) -sqlite3.OperationalError: database is locked - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 421, in run_asgi - result = await app( # type: ignore[func-returns-value] - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 56, in __call__ - return await self.app(scope, receive, send) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\applications.py", line 1159, in __call__ - await super().__call__(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\applications.py", line 90, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 186, in __call__ - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 164, in __call__ - await self.app(scope, receive, _send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\exceptions.py", line 63, in __call__ - await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__ - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 660, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 680, in app - await route.handle(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 276, in handle - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 134, in app - await wrap_app_handling_exceptions(app, request)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 120, in app - response = await f(request) - ^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 674, in app - raw_response = await run_endpoint_function( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 330, in run_endpoint_function - return await run_in_threadpool(dependant.call, **values) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\concurrency.py", line 32, in run_in_threadpool - return await anyio.to_thread.run_sync(func) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync - return await get_async_backend().run_sync_in_worker_thread( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 2518, in run_sync_in_worker_thread - return await future - ^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 1002, in run - result = context.run(func, *args) - ^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\api\routes.py", line 327, in reset_project - deleted = repo.reset_project_runtime_data(project.id) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\database\repository.py", line 113, in reset_project_runtime_data - .delete(synchronize_session=False) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\query.py", line 3217, in delete - self.session.execute( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 2351, in execute - return self._execute_internal( - ^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 2249, in _execute_internal - result: Result[Any] = compile_state_cls.orm_execute_statement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\bulk_persistence.py", line 2033, in orm_execute_statement - return super().orm_execute_statement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\context.py", line 306, in orm_execute_statement - result = conn.execute( - ^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute - return meth( - ^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\sql\elements.py", line 527, in _execute_on_connection - return connection._execute_clauseelement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement - ret = self._execute_context( - ^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context - return self._exec_single_context( - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context - self._handle_dbapi_exception( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 2363, in _handle_dbapi_exception - raise sqlalchemy_exception.with_traceback(exc_info[2]) from e - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context - self.dialect.do_execute( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 952, in do_execute - cursor.execute(statement, parameters) -sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked -[SQL: DELETE FROM feedback_logs WHERE feedback_logs.project_id = ?] -[parameters: (1,)] -(Background on this error at: https://sqlalche.me/e/20/e3q8) -ERROR: Exception in ASGI application -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context - self.dialect.do_execute( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 952, in do_execute - cursor.execute(statement, parameters) -sqlite3.OperationalError: database is locked - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 421, in run_asgi - result = await app( # type: ignore[func-returns-value] - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 56, in __call__ - return await self.app(scope, receive, send) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\applications.py", line 1159, in __call__ - await super().__call__(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\applications.py", line 90, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 186, in __call__ - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 164, in __call__ - await self.app(scope, receive, _send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\exceptions.py", line 63, in __call__ - await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__ - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 660, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 680, in app - await route.handle(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 276, in handle - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 134, in app - await wrap_app_handling_exceptions(app, request)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 120, in app - response = await f(request) - ^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 674, in app - raw_response = await run_endpoint_function( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 330, in run_endpoint_function - return await run_in_threadpool(dependant.call, **values) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\concurrency.py", line 32, in run_in_threadpool - return await anyio.to_thread.run_sync(func) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync - return await get_async_backend().run_sync_in_worker_thread( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 2518, in run_sync_in_worker_thread - return await future - ^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 1002, in run - result = context.run(func, *args) - ^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\api\routes.py", line 327, in reset_project - deleted = repo.reset_project_runtime_data(project.id) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\database\repository.py", line 113, in reset_project_runtime_data - .delete(synchronize_session=False) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\query.py", line 3217, in delete - self.session.execute( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 2351, in execute - return self._execute_internal( - ^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 2249, in _execute_internal - result: Result[Any] = compile_state_cls.orm_execute_statement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\bulk_persistence.py", line 2033, in orm_execute_statement - return super().orm_execute_statement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\context.py", line 306, in orm_execute_statement - result = conn.execute( - ^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute - return meth( - ^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\sql\elements.py", line 527, in _execute_on_connection - return connection._execute_clauseelement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement - ret = self._execute_context( - ^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context - return self._exec_single_context( - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context - self._handle_dbapi_exception( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 2363, in _handle_dbapi_exception - raise sqlalchemy_exception.with_traceback(exc_info[2]) from e - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context - self.dialect.do_execute( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 952, in do_execute - cursor.execute(statement, parameters) -sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked -[SQL: DELETE FROM feedback_logs WHERE feedback_logs.project_id = ?] -[parameters: (1,)] -(Background on this error at: https://sqlalche.me/e/20/e3q8) -ERROR: Exception in ASGI application -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1936, in _exec_single_context - self.dialect.do_executemany( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 949, in do_executemany - cursor.executemany(statement, parameters) -sqlite3.OperationalError: database is locked - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 421, in run_asgi - result = await app( # type: ignore[func-returns-value] - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 56, in __call__ - return await self.app(scope, receive, send) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\applications.py", line 1159, in __call__ - await super().__call__(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\applications.py", line 90, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 186, in __call__ - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 164, in __call__ - await self.app(scope, receive, _send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\exceptions.py", line 63, in __call__ - await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__ - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 660, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 680, in app - await route.handle(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 276, in handle - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 134, in app - await wrap_app_handling_exceptions(app, request)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 120, in app - response = await f(request) - ^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 674, in app - raw_response = await run_endpoint_function( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 330, in run_endpoint_function - return await run_in_threadpool(dependant.call, **values) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\concurrency.py", line 32, in run_in_threadpool - return await anyio.to_thread.run_sync(func) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync - return await get_async_backend().run_sync_in_worker_thread( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 2518, in run_sync_in_worker_thread - return await future - ^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 1002, in run - result = context.run(func, *args) - ^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\api\routes.py", line 434, in knowledge_gaps - return KnowledgeGapDetector(session).list_open(project.id, max(min(limit, 300), 1)) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 27, in list_open - self.detect(project_id, limit=limit) - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 22, in detect - gaps.extend(self._underconnected_entity_gaps(project_id)) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 119, in _underconnected_entity_gaps - self._upsert_gap( - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 188, in _upsert_gap - self.session.flush() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4331, in flush - self._flush(objects) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4466, in _flush - with util.safe_reraise(): - ^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\util\langhelpers.py", line 121, in __exit__ - raise exc_value.with_traceback(exc_tb) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4427, in _flush - flush_context.execute() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\unitofwork.py", line 466, in execute - rec.execute(self) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\unitofwork.py", line 642, in execute - util.preloaded.orm_persistence.save_obj( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\persistence.py", line 85, in save_obj - _emit_update_statements( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\persistence.py", line 912, in _emit_update_statements - c = connection.execute( - ^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute - return meth( - ^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\sql\elements.py", line 527, in _execute_on_connection - return connection._execute_clauseelement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement - ret = self._execute_context( - ^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context - return self._exec_single_context( - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context - self._handle_dbapi_exception( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 2363, in _handle_dbapi_exception - raise sqlalchemy_exception.with_traceback(exc_info[2]) from e - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1936, in _exec_single_context - self.dialect.do_executemany( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 949, in do_executemany - cursor.executemany(statement, parameters) -sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked -[SQL: UPDATE knowledge_gaps SET updated_at=? WHERE knowledge_gaps.id = ?] -[parameters: [('2026-05-13 04:00:37.529636', 2), ('2026-05-13 04:00:37.530642', 4), ('2026-05-13 04:00:37.530642', 5), ('2026-05-13 04:00:37.530642', 6), ('2026-05-13 04:00:37.530642', 7), ('2026-05-13 04:00:37.530642', 8), ('2026-05-13 04:00:37.531648', 9), ('2026-05-13 04:00:37.531648', 10) ... displaying 10 of 26 total bound parameter sets ... ('2026-05-13 04:00:37.535699', 31), ('2026-05-13 04:00:37.535699', 32)]] -(Background on this error at: https://sqlalche.me/e/20/e3q8) -ERROR: Exception in ASGI application -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1936, in _exec_single_context - self.dialect.do_executemany( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 949, in do_executemany - cursor.executemany(statement, parameters) -sqlite3.OperationalError: database is locked - -The above exception was the direct cause of the following exception: - -Traceback (most recent call last): - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 421, in run_asgi - result = await app( # type: ignore[func-returns-value] - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 56, in __call__ - return await self.app(scope, receive, send) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\applications.py", line 1159, in __call__ - await super().__call__(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\applications.py", line 90, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 186, in __call__ - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\errors.py", line 164, in __call__ - await self.app(scope, receive, _send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\middleware\exceptions.py", line 63, in __call__ - await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__ - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 660, in __call__ - await self.middleware_stack(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 680, in app - await route.handle(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\routing.py", line 276, in handle - await self.app(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 134, in app - await wrap_app_handling_exceptions(app, request)(scope, receive, send) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app - raise exc - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app - await app(scope, receive, sender) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 120, in app - response = await f(request) - ^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 674, in app - raw_response = await run_endpoint_function( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\fastapi\routing.py", line 330, in run_endpoint_function - return await run_in_threadpool(dependant.call, **values) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\starlette\concurrency.py", line 32, in run_in_threadpool - return await anyio.to_thread.run_sync(func) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\to_thread.py", line 63, in run_sync - return await get_async_backend().run_sync_in_worker_thread( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 2518, in run_sync_in_worker_thread - return await future - ^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\anyio\_backends\_asyncio.py", line 1002, in run - result = context.run(func, *args) - ^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\api\routes.py", line 434, in knowledge_gaps - return KnowledgeGapDetector(session).list_open(project.id, max(min(limit, 300), 1)) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 27, in list_open - self.detect(project_id, limit=limit) - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 22, in detect - gaps.extend(self._underconnected_entity_gaps(project_id)) - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 119, in _underconnected_entity_gaps - self._upsert_gap( - File "C:\Users\lasta\MyProject\AI\crawler_platform\app\core\ontology\gap_detector.py", line 188, in _upsert_gap - self.session.flush() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4331, in flush - self._flush(objects) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4466, in _flush - with util.safe_reraise(): - ^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\util\langhelpers.py", line 121, in __exit__ - raise exc_value.with_traceback(exc_tb) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\session.py", line 4427, in _flush - flush_context.execute() - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\unitofwork.py", line 466, in execute - rec.execute(self) - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\unitofwork.py", line 642, in execute - util.preloaded.orm_persistence.save_obj( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\persistence.py", line 85, in save_obj - _emit_update_statements( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\orm\persistence.py", line 912, in _emit_update_statements - c = connection.execute( - ^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute - return meth( - ^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\sql\elements.py", line 527, in _execute_on_connection - return connection._execute_clauseelement( - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement - ret = self._execute_context( - ^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context - return self._exec_single_context( - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context - self._handle_dbapi_exception( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 2363, in _handle_dbapi_exception - raise sqlalchemy_exception.with_traceback(exc_info[2]) from e - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\base.py", line 1936, in _exec_single_context - self.dialect.do_executemany( - File "C:\Users\lasta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\Lib\site-packages\sqlalchemy\engine\default.py", line 949, in do_executemany - cursor.executemany(statement, parameters) -sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked -[SQL: UPDATE knowledge_gaps SET updated_at=? WHERE knowledge_gaps.id = ?] -[parameters: [('2026-05-13 04:00:45.068406', 2), ('2026-05-13 04:00:45.069451', 4), ('2026-05-13 04:00:45.069451', 5), ('2026-05-13 04:00:45.069451', 6), ('2026-05-13 04:00:45.069451', 7), ('2026-05-13 04:00:45.069451', 8), ('2026-05-13 04:00:45.070461', 9), ('2026-05-13 04:00:45.070461', 10) ... displaying 10 of 26 total bound parameter sets ... ('2026-05-13 04:00:45.074511', 31), ('2026-05-13 04:00:45.074511', 32)]] -(Background on this error at: https://sqlalche.me/e/20/e3q8) diff --git a/.server-stdout.log b/.server-stdout.log index 94a08cc..c9724b1 100644 --- a/.server-stdout.log +++ b/.server-stdout.log @@ -1,227 +1,60 @@ -INFO: 127.0.0.1:55654 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:55656 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:55658 - "POST /crawl HTTP/1.1" 200 OK -INFO: 127.0.0.1:60702 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:63667 - "GET /static/assets/index-BkBg_FAU.css HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "GET /static/assets/index-BsoXaTIL.js HTTP/1.1" 200 OK -INFO: 127.0.0.1:60212 - "GET /favicon.ico HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:63667 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:63667 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:63667 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:52091 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:63120 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:52091 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:63667 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:60212 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:52091 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:52091 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:63120 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:63667 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:52091 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:63120 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60212 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:58507 - "GET /projects/perfume_subscription/knowledge-gaps?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "POST /extractors/models HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "POST /projects/reset HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:60212 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:52091 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:63667 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60212 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:52091 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:63667 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60212 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:51909 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:52091 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:63667 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:63120 - "GET /projects/perfume_subscription/knowledge-gaps?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:58507 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:59175 - "POST /crawl-site HTTP/1.1" 200 OK -INFO: 127.0.0.1:59175 - "GET /crawl-site/jobs/1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:59175 - "GET /crawl-site/jobs/1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:59175 - "GET /crawl-site/jobs/1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:59175 - "GET /crawl-site/jobs/1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60573 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60167 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54345 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:59175 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:53330 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:54345 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60573 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:59175 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60167 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:53330 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:54345 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:60573 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:62512 - "GET /projects/perfume_subscription/knowledge-gaps?limit=50 HTTP/1.1" 500 Internal Server Error -INFO: 127.0.0.1:54311 - "GET /crawl-site/jobs/1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:61649 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:65400 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:64587 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:61649 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:54311 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:58516 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:65400 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:54311 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:64587 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:58516 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:65400 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:61649 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54492 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:54231 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:51157 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:51157 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:51157 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:54317 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:64933 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:62007 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:54317 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:51157 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:62007 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:64933 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:54317 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:62007 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:51157 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54317 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:64933 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:54231 - "POST /extractors/models HTTP/1.1" 200 OK -INFO: 127.0.0.1:64937 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:51597 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:51597 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:51597 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:55174 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:56473 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55174 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:64026 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:56473 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:51597 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:64026 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55174 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:51597 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:56473 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:64026 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:55174 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:57269 - "POST /extractors/models HTTP/1.1" 200 OK -INFO: 127.0.0.1:61245 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:51571 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:51571 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:51571 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:51571 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:51571 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:51571 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:57549 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:57549 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:57549 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:57549 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:57549 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:57549 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60418 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET / HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:55778 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55778 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:55778 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55978 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "POST /extractors/models HTTP/1.1" 200 OK -INFO: 127.0.0.1:55246 - "GET /projects/perfume_subscription/knowledge-gaps?limit=50 HTTP/1.1" 500 Internal Server Error -INFO: 127.0.0.1:54063 - "GET /crawl-site/jobs/1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60064 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54063 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:53121 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:54063 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60064 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:53121 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:54063 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:60064 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:53121 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60064 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:53121 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54063 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "POST /projects/reset HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /projects HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /projects/perfume_subscription HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /ontology/perfume HTTP/1.1" 200 OK -INFO: 127.0.0.1:60812 - "GET /projects/perfume_subscription/ontology/proposals?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:53523 - "GET /projects/perfume_subscription/ontology/triples?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55554 - "GET /projects/perfume_subscription/ontology/registry HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /projects/perfume_subscription/pipeline HTTP/1.1" 200 OK -INFO: 127.0.0.1:53523 - "GET /projects/perfume_subscription/claims?limit=200&include_candidates=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:60812 - "GET /projects/perfume_subscription/entities?limit=100 HTTP/1.1" 200 OK -INFO: 127.0.0.1:60812 - "GET /projects/perfume_subscription/graph/query?kind=trend_summary HTTP/1.1" 200 OK -INFO: 127.0.0.1:53523 - "GET /projects/perfume_subscription/research/sessions?limit=20 HTTP/1.1" 200 OK -INFO: 127.0.0.1:55554 - "GET /projects/perfume_subscription/claims?limit=200&status=rejected HTTP/1.1" 200 OK -INFO: 127.0.0.1:60812 - "GET /projects/perfume_subscription/recommendation-tags HTTP/1.1" 200 OK -INFO: 127.0.0.1:53523 - "GET /projects/perfume_subscription/graph/neighborhood?limit=300 HTTP/1.1" 200 OK -INFO: 127.0.0.1:61169 - "GET /projects/perfume_subscription/extraction-logs?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54030 - "GET /projects/perfume_subscription/knowledge-gaps?limit=50 HTTP/1.1" 200 OK -INFO: 127.0.0.1:54030 - "GET /crawl-site/jobs/1 HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:57352 - "GET /projects/perfume_subscription/knowledge-gaps?limit=50 HTTP/1.1" 500 Internal Server Error -INFO: 127.0.0.1:54030 - "GET /crawl-site/jobs/1 HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:54030 - "GET /crawl-site/jobs/1 HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:54868 - "GET /projects/perfume_subscription/knowledge-gaps?limit=50 HTTP/1.1" 500 Internal Server Error -INFO: 127.0.0.1:54030 - "GET /crawl-site/jobs/1 HTTP/1.1" 404 Not Found -INFO: 127.0.0.1:54030 - "GET /crawl-site/jobs/1 HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:51497 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:51498 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:51500 - "GET /static/locales/ko/common.json HTTP/1.1" 200 OK +INFO: 127.0.0.1:55124 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:55124 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:52395 - "GET /static/assets/index-uHGS9kYH.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:55124 - "GET /static/locales/ko/common.json HTTP/1.1" 200 OK +INFO: 127.0.0.1:52395 - "GET /static/locales/ko/pages.json HTTP/1.1" 200 OK +INFO: 127.0.0.1:50896 - "GET /static/locales/en/common.json HTTP/1.1" 200 OK +INFO: 127.0.0.1:60772 - "GET /static/locales/en/pages.json HTTP/1.1" 200 OK +INFO: 127.0.0.1:50896 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:60772 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:57787 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:55691 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:57787 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:51064 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:57726 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/locales/ko/pages.json HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/locales/en/pages.json HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/assets/index-uHGS9kYH.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:53782 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:62676 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/assets/index-uHGS9kYH.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:53782 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:51077 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/assets/index-uHGS9kYH.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:53782 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:51077 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/assets/index-uHGS9kYH.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:53782 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:51077 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/assets/index-uHGS9kYH.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:53782 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:51077 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/crawl/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:51077 - "GET /static/assets/index-uHGS9kYH.css HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /static/assets/index-DmWQTzXA.js HTTP/1.1" 200 OK +INFO: 127.0.0.1:53782 - "GET /favicon.ico HTTP/1.1" 404 Not Found +INFO: 127.0.0.1:51077 - "GET /projects HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /projects/PerfumeSubscribe_new HTTP/1.1" 200 OK +INFO: 127.0.0.1:62676 - "GET /domains HTTP/1.1" 200 OK diff --git a/README.md b/README.md index 72b9228..d2a96d5 100644 --- a/README.md +++ b/README.md @@ -1,234 +1,81 @@ -# Ontology Platform +# Ontology Platform -온톨로지 플랫폼은 웹에서 구조화된 지식(엔티티/관계)을 자동 추출, 검증, 저장하는 고속 시스템입니다. +본 프로젝트는 여러 검증된 오픈소스 기반 엔진들을 조합하여 구축된 온톨로지 구축 플랫폼이다. -**Phase 0-4** 전체 구현 완료 | 추출(10초) → 검증(<100ms) → 그래프 저장 → 벡터 검색 +목표는 기존 엔진을 폐기하거나 새로 만드는 것이 아니라: -## 🚀 빠른 시작 +- 기존 엔진을 최대한 유지하면서 +- 부족한 플랫폼 기능을 확장하고 +- 온톨로지 구축 워크플로우를 강화하며 +- 상용화 가능한 구조로 발전시키는 것이다. -### 1. 설치 - -```bash -# 기본 설치 (Phase 0-1: 추출) -pip install fastapi uvicorn pydantic trafilatura httpx - -# Phase 2 추가 (동적 페이지) -pip install crawl4ai - -# Phase 4 추가 (Neo4j) -pip install neo4j sentence-transformers -``` - -### 2. Phase 0-1만 사용 (가장 간단) - -```bash -# API 서버 시작 -python -m uvicorn ontology_platform.ont_platform.api.phase0_app:app --reload - -# URL에서 추출 -curl -X POST "http://localhost:8000/api/v1/extract/url?url=https://example.com" -``` - -### 3. Phase 4 (그래프 검색) 포함 - -```bash -# Neo4j 시작 -docker-compose -f docker-compose.neo4j.yml up -d - -# API 서버 시작 -python -m uvicorn ontology_platform.ont_platform.api.phase0_app:app --reload - -# 추출 → 수집 → 검색 -curl -X POST "http://localhost:8000/api/v1/extract/url?url=https://example.com" -curl -X POST "http://localhost:8000/api/v1/search/ingest" -d '{"entities": [...], "relations": [...]}' -curl "http://localhost:8000/api/v1/search/vector?query=machine+learning" -``` - -## 📋 Phase별 기능 - -| Phase | 기능 | 시간 | 상태 | -|-------|------|------|------| -| 0-1 | HTML 추출 (Trafilatura) | 10-15초 | ✅ | -| 2 | 동적 페이지 (Crawl4AI) | 20-30초 | ✅ | -| 3A | 경량 검증 (Pydantic) | <100ms | ✅ | -| 3B | SPARQL 검증 | <500ms | ✅ | -| 4 | Neo4j + 벡터 검색 | 50-200ms | ✅ | - -## 🎯 사용 예시 - -### 예시 1: 기본 추출 (10초) - -```bash -curl -X POST "http://localhost:8000/api/v1/extract/url?url=https://wikipedia.org/wiki/Python" -``` - -응답: -```json -{ - "url": "https://wikipedia.org/wiki/Python", - "title": "Python - Wikipedia", - "entities": [ - { - "id": "E_1", - "label": "Python", - "type": "ProgrammingLanguage", - "confidence": 0.95 - } - ], - "relations": [...], - "extraction_time_sec": 9.5, - "validation_passed": true -} -``` - -### 예시 2: 동적 페이지 (25초) - -```bash -curl -X POST "http://localhost:8000/api/v1/extract/url?url=https://app.example.com&profile=dynamic_page" -``` - -### 예시 3: 그래프 수집 + 검색 - -```bash -# 1. 추출 -RESULT=$(curl -s -X POST "http://localhost:8000/api/v1/extract/url?url=https://example.com") - -# 2. Neo4j에 수집 -curl -X POST "http://localhost:8000/api/v1/search/ingest" \ - -H "Content-Type: application/json" \ - -d "{\"entities\": $(echo $RESULT | jq '.entities'), \"relations\": $(echo $RESULT | jq '.relations')}" - -# 3. 벡터 검색 -curl "http://localhost:8000/api/v1/search/vector?query=programming&limit=10" - -# 4. 그래프 통계 -curl "http://localhost:8000/api/v1/search/stats" - -# 5. 엔티티 이웃 -curl "http://localhost:8000/api/v1/search/entity/E_1?depth=1" -``` - -## 🔧 설정 - -### Phase 선택 (validators.py) - -```python -# 경량 검증 (기본) -guard = OntologyGuard(validator_type="lightweight") - -# SPARQL 검증 -guard = OntologyGuard(validator_type="ontocast") -``` - -### Neo4j 연결 (neo4j_adapter.py) - -```python -# 기본값 -config = Neo4jConfig() # localhost:7687 - -# 커스텀 -config = Neo4jConfig( - uri="bolt://custom-host:7687", - username="user", - password="pass", - database="mydb" -) -adapter = Neo4jAdapter(config=config) -``` - -## 📊 API 문서 - -서버 시작 후: -- **Swagger UI**: http://localhost:8000/docs -- **ReDoc**: http://localhost:8000/redoc - -### 주요 엔드포인트 - -``` -POST /api/v1/extract/url 추출 -GET /api/v1/search/stats 통계 -POST /api/v1/search/vector 벡터 검색 -GET /api/v1/search/entity/{id} 이웃 탐색 -POST /api/v1/search/ingest 그래프 수집 -``` - -## 🧪 테스트 - -```bash -# Phase 0-1 -python test_phase0_extraction.py - -# Phase 2 -python test_phase2_crawl.py - -# Phase 3A -python test_phase3_validation.py - -# Phase 3B -python test_phase3_option_b.py - -# Phase 4 -python test_phase4_integration.py -``` - -## 📦 의존성 - -- **FastAPI**: API 프레임워크 -- **Trafilatura**: HTML 추출 -- **Crawl4AI**: 동적 크롤링 (선택) -- **Pydantic**: 데이터 검증 -- **Neo4j**: 그래프 DB (선택) -- **SentenceTransformers**: 벡터 임베딩 (선택) - -## 🐳 Docker - -```bash -# Neo4j만 -docker-compose -f docker-compose.neo4j.yml up -d - -# 전체 스택 (향후) -docker-compose up -d -``` - -## 📚 상세 문서 - -- [구현 요약](IMPLEMENTATION_SUMMARY.md) - Phase 0-4 전체 개요 -- [Phase 2](PHASE2_COMPLETION.md) - Crawl4AI 동적 크롤링 -- [Phase 3A](PHASE3_COMPLETION.md) - 경량 검증 -- [Phase 3B](PHASE3_OPTION_B.md) - SPARQL 검증 -- [Phase 4](PHASE4_COMPLETION.md) - Neo4j 그래프 + 벡터 검색 - -## 🎓 설계 원칙 - -1. **Phase-gated**: 각 Phase는 선택사항 -2. **Pluggable**: 여러 검증 방식 지원 -3. **Async**: 높은 동시성 -4. **Resilient**: 의존성 부재 시에도 동작 - -## 💡 다음 단계 - -### Phase 5: GraphRAG (선택) -- RDF ↔ Property Graph 변환 -- Entity Resolver -- Subgraph retrieval - -### Advanced Features -- Critic loop (자동 수정) -- Few-shot learning -- Zero-shot 분류 - -## 🔗 관련 링크 - -- [Neo4j 문서](https://neo4j.com/docs/) -- [SentenceTransformers](https://www.sbert.net/) -- [Trafilatura](https://trafilatura.python-engineering.com/) -- [FastAPI](https://fastapi.tiangolo.com/) - -## 📝 라이센스 - -MIT License +기존 엔진은 이미 검증된 기반 자산으로 간주한다. --- -**Version**: 0.4.0 (Phase 0-4 완료) -**Updated**: 2026-05-14 +# CORE DEVELOPMENT PRINCIPLES + +## 기존 엔진 재사용 원칙 + +현재 시스템은 다음과 같은 검증된 오픈소스 기반 구조들을 포함한다. + +- 웹 크롤링 엔진 +- HTML 정제 시스템 +- 엔티티 추출 구조 +- 클레임 생성 구조 +- 데이터 저장 구조 +- 기존 분석 파이프라인 + +따라서: + +- 기존 엔진을 임의로 교체하지 않는다. +- 없는 기능만 확장한다. +- 구조적 문제가 확인된 경우에만 수정 검토한다. +- 승인 없는 대규모 리팩토링을 금지한다. +- 전체 구조 재설계를 금지한다. + +--- + +# TASK EXECUTION MODES + +프로젝트 작업 모드는 세 가지로 구분된다. + +--- + +## 1. PHASE WORKFLOW MODE + +로드맵 기반 작업 수행 모드. +다음 명령이 포함될 때만 활성화. + +- ^(다음 )?(페이즈|PHASE|phase)( \d+(-\d+)?)? 진행$ + +이 경우에만 +PHASE_WORKFLOW.MD 를 읽고 작업을 진행한다. + +--- + +## 2. PLANNING MODE + +설계 및 roadmap 생성 모드. +다음 명령이 포함될 때만 활성화. + +- ^(페이즈|PHASE|phase)( \d+(-\d+)?)? (생성|계획)$ + +이 경우에만 +PHASE_PLANNING.md 를 읽고 작업을 진행한다. + +--- + +# PROJECT STRUCTURE + +```text +README.md + +/docs/phases + PHASE_WORKFLOW.md + PHASE_PLANNING.md + PHASE_INDEX.md + + /phase_01 + /phase_02 + /phase_03 \ No newline at end of file diff --git a/README_old.md b/README_old.md new file mode 100644 index 0000000..72b9228 --- /dev/null +++ b/README_old.md @@ -0,0 +1,234 @@ +# Ontology Platform + +온톨로지 플랫폼은 웹에서 구조화된 지식(엔티티/관계)을 자동 추출, 검증, 저장하는 고속 시스템입니다. + +**Phase 0-4** 전체 구현 완료 | 추출(10초) → 검증(<100ms) → 그래프 저장 → 벡터 검색 + +## 🚀 빠른 시작 + +### 1. 설치 + +```bash +# 기본 설치 (Phase 0-1: 추출) +pip install fastapi uvicorn pydantic trafilatura httpx + +# Phase 2 추가 (동적 페이지) +pip install crawl4ai + +# Phase 4 추가 (Neo4j) +pip install neo4j sentence-transformers +``` + +### 2. Phase 0-1만 사용 (가장 간단) + +```bash +# API 서버 시작 +python -m uvicorn ontology_platform.ont_platform.api.phase0_app:app --reload + +# URL에서 추출 +curl -X POST "http://localhost:8000/api/v1/extract/url?url=https://example.com" +``` + +### 3. Phase 4 (그래프 검색) 포함 + +```bash +# Neo4j 시작 +docker-compose -f docker-compose.neo4j.yml up -d + +# API 서버 시작 +python -m uvicorn ontology_platform.ont_platform.api.phase0_app:app --reload + +# 추출 → 수집 → 검색 +curl -X POST "http://localhost:8000/api/v1/extract/url?url=https://example.com" +curl -X POST "http://localhost:8000/api/v1/search/ingest" -d '{"entities": [...], "relations": [...]}' +curl "http://localhost:8000/api/v1/search/vector?query=machine+learning" +``` + +## 📋 Phase별 기능 + +| Phase | 기능 | 시간 | 상태 | +|-------|------|------|------| +| 0-1 | HTML 추출 (Trafilatura) | 10-15초 | ✅ | +| 2 | 동적 페이지 (Crawl4AI) | 20-30초 | ✅ | +| 3A | 경량 검증 (Pydantic) | <100ms | ✅ | +| 3B | SPARQL 검증 | <500ms | ✅ | +| 4 | Neo4j + 벡터 검색 | 50-200ms | ✅ | + +## 🎯 사용 예시 + +### 예시 1: 기본 추출 (10초) + +```bash +curl -X POST "http://localhost:8000/api/v1/extract/url?url=https://wikipedia.org/wiki/Python" +``` + +응답: +```json +{ + "url": "https://wikipedia.org/wiki/Python", + "title": "Python - Wikipedia", + "entities": [ + { + "id": "E_1", + "label": "Python", + "type": "ProgrammingLanguage", + "confidence": 0.95 + } + ], + "relations": [...], + "extraction_time_sec": 9.5, + "validation_passed": true +} +``` + +### 예시 2: 동적 페이지 (25초) + +```bash +curl -X POST "http://localhost:8000/api/v1/extract/url?url=https://app.example.com&profile=dynamic_page" +``` + +### 예시 3: 그래프 수집 + 검색 + +```bash +# 1. 추출 +RESULT=$(curl -s -X POST "http://localhost:8000/api/v1/extract/url?url=https://example.com") + +# 2. Neo4j에 수집 +curl -X POST "http://localhost:8000/api/v1/search/ingest" \ + -H "Content-Type: application/json" \ + -d "{\"entities\": $(echo $RESULT | jq '.entities'), \"relations\": $(echo $RESULT | jq '.relations')}" + +# 3. 벡터 검색 +curl "http://localhost:8000/api/v1/search/vector?query=programming&limit=10" + +# 4. 그래프 통계 +curl "http://localhost:8000/api/v1/search/stats" + +# 5. 엔티티 이웃 +curl "http://localhost:8000/api/v1/search/entity/E_1?depth=1" +``` + +## 🔧 설정 + +### Phase 선택 (validators.py) + +```python +# 경량 검증 (기본) +guard = OntologyGuard(validator_type="lightweight") + +# SPARQL 검증 +guard = OntologyGuard(validator_type="ontocast") +``` + +### Neo4j 연결 (neo4j_adapter.py) + +```python +# 기본값 +config = Neo4jConfig() # localhost:7687 + +# 커스텀 +config = Neo4jConfig( + uri="bolt://custom-host:7687", + username="user", + password="pass", + database="mydb" +) +adapter = Neo4jAdapter(config=config) +``` + +## 📊 API 문서 + +서버 시작 후: +- **Swagger UI**: http://localhost:8000/docs +- **ReDoc**: http://localhost:8000/redoc + +### 주요 엔드포인트 + +``` +POST /api/v1/extract/url 추출 +GET /api/v1/search/stats 통계 +POST /api/v1/search/vector 벡터 검색 +GET /api/v1/search/entity/{id} 이웃 탐색 +POST /api/v1/search/ingest 그래프 수집 +``` + +## 🧪 테스트 + +```bash +# Phase 0-1 +python test_phase0_extraction.py + +# Phase 2 +python test_phase2_crawl.py + +# Phase 3A +python test_phase3_validation.py + +# Phase 3B +python test_phase3_option_b.py + +# Phase 4 +python test_phase4_integration.py +``` + +## 📦 의존성 + +- **FastAPI**: API 프레임워크 +- **Trafilatura**: HTML 추출 +- **Crawl4AI**: 동적 크롤링 (선택) +- **Pydantic**: 데이터 검증 +- **Neo4j**: 그래프 DB (선택) +- **SentenceTransformers**: 벡터 임베딩 (선택) + +## 🐳 Docker + +```bash +# Neo4j만 +docker-compose -f docker-compose.neo4j.yml up -d + +# 전체 스택 (향후) +docker-compose up -d +``` + +## 📚 상세 문서 + +- [구현 요약](IMPLEMENTATION_SUMMARY.md) - Phase 0-4 전체 개요 +- [Phase 2](PHASE2_COMPLETION.md) - Crawl4AI 동적 크롤링 +- [Phase 3A](PHASE3_COMPLETION.md) - 경량 검증 +- [Phase 3B](PHASE3_OPTION_B.md) - SPARQL 검증 +- [Phase 4](PHASE4_COMPLETION.md) - Neo4j 그래프 + 벡터 검색 + +## 🎓 설계 원칙 + +1. **Phase-gated**: 각 Phase는 선택사항 +2. **Pluggable**: 여러 검증 방식 지원 +3. **Async**: 높은 동시성 +4. **Resilient**: 의존성 부재 시에도 동작 + +## 💡 다음 단계 + +### Phase 5: GraphRAG (선택) +- RDF ↔ Property Graph 변환 +- Entity Resolver +- Subgraph retrieval + +### Advanced Features +- Critic loop (자동 수정) +- Few-shot learning +- Zero-shot 분류 + +## 🔗 관련 링크 + +- [Neo4j 문서](https://neo4j.com/docs/) +- [SentenceTransformers](https://www.sbert.net/) +- [Trafilatura](https://trafilatura.python-engineering.com/) +- [FastAPI](https://fastapi.tiangolo.com/) + +## 📝 라이센스 + +MIT License + +--- + +**Version**: 0.4.0 (Phase 0-4 완료) +**Updated**: 2026-05-14 diff --git a/crawler_platform.db b/crawler_platform.db index 2860ee0..d10a1bb 100644 Binary files a/crawler_platform.db and b/crawler_platform.db differ diff --git a/crawler_platform/app/api/routes.py b/crawler_platform/app/api/routes.py index faa64d8..8b09b30 100644 --- a/crawler_platform/app/api/routes.py +++ b/crawler_platform/app/api/routes.py @@ -1,9 +1,12 @@ from __future__ import annotations +import csv +import io +import json from dataclasses import asdict from typing import Any -from fastapi import BackgroundTasks, HTTPException +from fastapi import BackgroundTasks, HTTPException, Response from pydantic import BaseModel, Field from sqlalchemy import select @@ -253,6 +256,35 @@ class BulkClaimStatusRequest(BaseModel): reason: str | None = None +class SchemaEntityTypeRequest(BaseModel): + name: str + domain: str = "generic" + description: str | None = None + status: str = "active" + confidence: float = 1.0 + metadata: dict[str, Any] = Field(default_factory=dict) + + +class SchemaRelationTypeRequest(BaseModel): + name: str + domain: str = "generic" + description: str | None = None + allowed_subject_types: list[str] = Field(default_factory=list) + allowed_object_types: list[str] = Field(default_factory=list) + allowed_page_types: list[str] = Field(default_factory=list) + allowed_source_zones: list[str] = Field(default_factory=list) + semantic_constraints: dict[str, Any] = Field(default_factory=dict) + confidence_rules: dict[str, Any] = Field(default_factory=dict) + min_confidence: float | None = None + status: str = "active" + confidence: float = 1.0 + metadata: dict[str, Any] = Field(default_factory=dict) + + +class PipelineRerunRequest(BaseModel): + stage: str + + class MergeEntitiesRequest(BaseModel): project_name: str source_entity_id: int @@ -316,6 +348,16 @@ def _apply_claim_review(claim: models.Claim, status: str, reason: str | None) -> claim.last_seen_at = models.utcnow() +def turtle_id(value: Any) -> str: + text = "".join(ch if ch.isalnum() else "_" for ch in str(value or "").strip()) + text = "_".join(part for part in text.split("_") if part) + if not text: + return "value" + if text[0].isdigit(): + text = f"n_{text}" + return text[:120] + + def apply_crawl_request_overrides(config, request: CrawlRequest | DiscoverRequest) -> None: check_robots_txt = request.respect_robots_txt if check_robots_txt is None: @@ -596,6 +638,61 @@ def register_routes(app, database_url: str) -> None: project = KnowledgeRepository(session).get_project(project_name) return OntologyRegistry(session).registry_payload(project.id) + @app.post("/projects/{project_name}/schema/entity-types") + def create_schema_entity_type(project_name: str, request: SchemaEntityTypeRequest): + with session_scope(database_url) as session: + project = KnowledgeRepository(session).get_project(project_name) + row = OntologyRegistry(session).upsert_entity_type( + project.id, + name=request.name, + domain=request.domain, + description=request.description, + status=request.status, + confidence=min(max(request.confidence, 0.0), 1.0), + metadata={"origin": "manual_schema_designer", **request.metadata}, + ) + return { + "id": row.id, + "name": row.name, + "domain": row.domain, + "description": row.description, + "status": row.status, + "confidence": row.confidence, + } + + @app.post("/projects/{project_name}/schema/relation-types") + def create_schema_relation_type(project_name: str, request: SchemaRelationTypeRequest): + confidence_rules = dict(request.confidence_rules or {}) + if request.min_confidence is not None: + confidence_rules["min_confidence"] = min(max(request.min_confidence, 0.0), 1.0) + with session_scope(database_url) as session: + project = KnowledgeRepository(session).get_project(project_name) + row = OntologyRegistry(session).upsert_relation_type( + project.id, + name=request.name, + domain=request.domain, + description=request.description, + allowed_subject_types=request.allowed_subject_types, + allowed_object_types=request.allowed_object_types, + allowed_page_types=request.allowed_page_types, + allowed_source_zones=request.allowed_source_zones, + semantic_constraints=request.semantic_constraints, + confidence_rules=confidence_rules, + status=request.status, + confidence=min(max(request.confidence, 0.0), 1.0), + metadata={"origin": "manual_schema_designer", **request.metadata}, + ) + return { + "id": row.id, + "name": row.name, + "domain": row.domain, + "description": row.description, + "allowed_subject_types": row.allowed_subject_types or [], + "allowed_object_types": row.allowed_object_types or [], + "status": row.status, + "confidence": row.confidence, + } + @app.get("/projects/{project_name}/ontology/proposals") def ontology_proposals(project_name: str, limit: int = 100): with session_scope(database_url) as session: @@ -655,6 +752,127 @@ def register_routes(app, database_url: str) -> None: ) return results + @app.get("/projects/{project_name}/export") + def export_ontology( + project_name: str, + format: str = "json", + status: str = "validated_claim", + include_evidence: bool = True, + limit: int = 1000, + ): + fmt = format.strip().lower() + bounded_limit = max(min(limit, 5000), 1) + with session_scope(database_url) as session: + project = KnowledgeRepository(session).get_project(project_name) + query = ( + select(models.Claim, models.Source, models.Page, models.Entity) + .join(models.Source, models.Claim.source_id == models.Source.id) + .join(models.Page, models.Claim.page_id == models.Page.id, isouter=True) + .join(models.Entity, models.Claim.subject_entity_id == models.Entity.id) + .where(models.Claim.project_id == project.id) + ) + if status and status != "all": + normalized = _normalize_claim_status(status) or status + query = query.where(models.Claim.status == normalized) + rows = session.execute( + query.order_by(models.Claim.confidence.desc(), models.Claim.last_seen_at.desc()).limit(bounded_limit) + ).all() + items: list[dict[str, Any]] = [] + for claim, source, page, subject in rows: + object_entity = session.get(models.Entity, claim.object_entity_id) if claim.object_entity_id else None + evidence = None + if include_evidence: + evidence = session.scalar( + select(models.Evidence) + .where(models.Evidence.claim_id == claim.id) + .order_by(models.Evidence.created_at.desc()) + ) + items.append( + { + "claim_id": claim.id, + "subject": subject.name, + "subject_type": subject.entity_type, + "predicate": claim.predicate, + "object": object_entity.name if object_entity else claim.object_value, + "object_type": object_entity.entity_type if object_entity else claim.value_type, + "status": claim.status, + "confidence": claim.confidence, + "source": source.name, + "source_url": page.url if page else None, + "evidence_text": evidence.evidence_text if evidence else None, + "created_by": claim.extraction_method, + "last_seen_at": claim.last_seen_at.isoformat() if claim.last_seen_at else None, + } + ) + + filename = f"{project_name}_ontology.{fmt if fmt != 'turtle' else 'ttl'}" + if fmt == "json": + return { + "project": project_name, + "status": status, + "count": len(items), + "claims": items, + } + if fmt == "csv": + output = io.StringIO() + fieldnames = [ + "claim_id", + "subject", + "subject_type", + "predicate", + "object", + "object_type", + "status", + "confidence", + "source", + "source_url", + "evidence_text", + "created_by", + "last_seen_at", + ] + writer = csv.DictWriter(output, fieldnames=fieldnames) + writer.writeheader() + for item in items: + row = {key: json.dumps(value, ensure_ascii=False) if isinstance(value, (dict, list)) else value for key, value in item.items()} + writer.writerow(row) + return Response( + content=output.getvalue(), + media_type="text/csv; charset=utf-8", + headers={"Content-Disposition": f'attachment; filename="{filename}"'}, + ) + if fmt in {"ttl", "turtle"}: + lines = [ + "@prefix ont: .", + "@prefix claim: .", + "@prefix xsd: .", + "", + ] + for item in items: + sid = turtle_id(item["subject"]) + predicate = turtle_id(item["predicate"]) + obj = item["object"] + if isinstance(obj, str) and obj.strip(): + object_repr = f'ont:{turtle_id(obj)}' + else: + object_repr = json.dumps(obj, ensure_ascii=False) + lines.extend( + [ + f"ont:{sid} ont:{predicate} {object_repr} .", + f"claim:c{item['claim_id']} ont:confidence \"{item['confidence']}\"^^xsd:decimal .", + ] + ) + if item.get("source_url"): + lines.append(f"claim:c{item['claim_id']} ont:sourceUrl {json.dumps(item['source_url'], ensure_ascii=False)} .") + if item.get("evidence_text"): + lines.append(f"claim:c{item['claim_id']} ont:evidenceText {json.dumps(item['evidence_text'], ensure_ascii=False)} .") + lines.append("") + return Response( + content="\n".join(lines), + media_type="text/turtle; charset=utf-8", + headers={"Content-Disposition": f'attachment; filename="{filename}"'}, + ) + raise HTTPException(status_code=400, detail=f"unsupported export format: {format}") + @app.get("/projects/{project_name}/knowledge-gaps") def knowledge_gaps(project_name: str, limit: int = 100): with session_scope(database_url) as session: @@ -942,10 +1160,30 @@ def register_routes(app, database_url: str) -> None: return research_session_payload(job) @app.get("/projects/{project_name}/graph/neighborhood") - def graph_neighborhood(project_name: str, entity_id: int | None = None, limit: int = 120): + def graph_neighborhood( + project_name: str, + entity_id: int | None = None, + limit: int = 120, + include_candidates: bool = False, + status: str | None = None, + ): with session_scope(database_url) as session: project = KnowledgeRepository(session).get_project(project_name) - return SemanticGraphQuery(session).neighborhood(project.id, entity_id, max(min(limit, 300), 1)) + if status and status != "all": + normalized = _normalize_claim_status(status) or status + statuses = [normalized] + elif status == "all": + statuses = None + elif include_candidates: + statuses = ["validated_claim", "active", "candidate_claim", "rule_candidate"] + else: + statuses = ["validated_claim"] + return SemanticGraphQuery(session).neighborhood( + project.id, + entity_id, + max(min(limit, 300), 1), + statuses=statuses, + ) @app.get("/projects/{project_name}/graph/query") def graph_query( @@ -1182,9 +1420,11 @@ def register_routes(app, database_url: str) -> None: results: list[dict[str, Any]] = [] for claim, source, page, subject in rows: object_name = None + object_type = None if claim.object_entity_id: object_entity = session.get(models.Entity, claim.object_entity_id) object_name = object_entity.name if object_entity else None + object_type = object_entity.entity_type if object_entity else None evidence = session.scalar( select(models.Evidence) .where(models.Evidence.claim_id == claim.id) @@ -1197,12 +1437,14 @@ def register_routes(app, database_url: str) -> None: "subject_type": subject.entity_type, "predicate": claim.predicate, "object": object_name, + "object_type": object_type, "object_value": claim.object_value, "source": source.name, "page_url": page.url if page else None, "confidence": claim.confidence, "confidence_reason": claim.confidence_reason, "status": claim.status, + "extraction_method": claim.extraction_method, "evidence_text": evidence.evidence_text if evidence else None, "evidence_summary": evidence.evidence_summary if evidence else None, "page_type": (claim.metadata_json or {}).get("page_type") @@ -1313,6 +1555,126 @@ def register_routes(app, database_url: str) -> None: ], } + @app.post("/projects/{project_name}/pipeline/rerun") + def rerun_pipeline_stage( + project_name: str, + request: PipelineRerunRequest, + background_tasks: BackgroundTasks, + ): + crawl_stages = { + "source-crawl", + "page-clean", + "page-classification", + "entity-extraction", + "claim-generation", + "deduplication", + "validation", + } + route_by_stage = { + "human-review": f"/review/{project_name}", + "ontology-commit": f"/graph/{project_name}", + "export": f"/export/{project_name}", + } + if request.stage not in crawl_stages: + return { + "ok": True, + "stage": request.stage, + "action": "navigate", + "route": route_by_stage.get(request.stage, f"/pipeline/{project_name}"), + "message": "This stage is controlled from its workspace screen.", + } + + with session_scope(database_url) as session: + repo = KnowledgeRepository(session) + project = repo.get_project(project_name) + config = project_config_from_project_row(session, project) + latest_job = session.scalar( + select(models.CrawlJob) + .where(models.CrawlJob.project_id == project.id) + .order_by(models.CrawlJob.scheduled_at.desc()) + ) + latest_request = dict((latest_job.metadata_json or {}).get("request") or {}) if latest_job else {} + source_name = latest_request.get("source_name") + source = None + if source_name: + source = session.scalar( + select(models.Source).where( + models.Source.project_id == project.id, + models.Source.name == source_name, + ) + ) + if source is None: + source = session.scalar( + select(models.Source) + .where(models.Source.project_id == project.id) + .order_by(models.Source.updated_at.desc()) + ) + if source is None: + raise HTTPException(status_code=400, detail="No source is registered for this project.") + url = latest_request.get("url") or source.base_url + if not url: + raise HTTPException(status_code=400, detail="No previous crawl URL or source base_url is available.") + + inner_request = SiteCrawlRequest( + config_path="", + source_name=source.name, + url=url, + extractor_provider=str(latest_request.get("extractor_provider") or "lm_studio"), + extractor_model=latest_request.get("extractor_model"), + extractor_base_url=latest_request.get("extractor_base_url") or "http://localhost:1234/v1", + check_robots_txt=bool(latest_request.get("check_robots_txt") or False), + respect_robots_txt=latest_request.get("respect_robots_txt"), + max_depth=int(latest_request.get("max_depth") or 2), + max_pages=int(latest_request.get("max_pages") or 50), + same_domain_only=bool(latest_request.get("same_domain_only", True)), + analyze_page_types=list( + latest_request.get("analyze_page_types") + or ["ProductPage", "BrandStoryPage", "ReviewPage"] + ), + ) + apply_crawl_request_overrides(config, inner_request) + job = models.CrawlJob( + project_id=project.id, + source_id=source.id, + url=url, + status="pending", + metadata_json={ + "kind": "site_crawl", + "request": inner_request.model_dump(), + "project_name": project_name, + "rerun_stage": request.stage, + "progress": { + "seed_url": url, + "visited_count": 0, + "analyzed_count": 0, + "queued_count": 1, + "skipped_count": 0, + "errors": [], + "pages": [], + }, + }, + ) + session.add(job) + session.flush() + response = crawl_job_response(job) + + task_payload = { + **inner_request.model_dump(), + "__config_dict": project_config_to_dict(config), + } + background_tasks.add_task( + run_site_crawl_job, + database_url, + response["job_id"], + task_payload, + ) + return { + **response, + "ok": True, + "stage": request.stage, + "action": "job_started", + } + @app.get("/projects/{project_name}/search") def project_search(project_name: str, q: str = "", limit: int = 10): q = (q or "").strip() diff --git a/crawler_platform/app/core/research/graph_query.py b/crawler_platform/app/core/research/graph_query.py index 5b3b9cb..eb9a04c 100644 --- a/crawler_platform/app/core/research/graph_query.py +++ b/crawler_platform/app/core/research/graph_query.py @@ -12,12 +12,20 @@ class SemanticGraphQuery: def __init__(self, session: Session): self.session = session - def neighborhood(self, project_id: int, entity_id: int | None = None, limit: int = 120) -> dict[str, Any]: + def neighborhood( + self, + project_id: int, + entity_id: int | None = None, + limit: int = 120, + statuses: list[str] | None = None, + ) -> dict[str, Any]: query = ( select(models.Claim, models.Entity) .join(models.Entity, models.Claim.subject_entity_id == models.Entity.id) - .where(models.Claim.project_id == project_id, models.Claim.status == "validated_claim") + .where(models.Claim.project_id == project_id) ) + if statuses is not None: + query = query.where(models.Claim.status.in_(statuses)) if entity_id is not None: query = query.where( (models.Claim.subject_entity_id == entity_id) | (models.Claim.object_entity_id == entity_id) @@ -40,7 +48,9 @@ class SemanticGraphQuery: "target": claim.object_entity_id, "target_value": claim.object_value if not claim.object_entity_id else None, "predicate": claim.predicate, + "status": claim.status, "confidence": claim.confidence, + "last_seen_at": claim.last_seen_at.isoformat() if claim.last_seen_at else None, "metadata": claim.metadata_json or {}, "object": object_payload, } diff --git a/crawler_platform/app/main.py b/crawler_platform/app/main.py index a10aa69..a4d0c3c 100644 --- a/crawler_platform/app/main.py +++ b/crawler_platform/app/main.py @@ -5,7 +5,6 @@ from pathlib import Path from fastapi import FastAPI, Request from fastapi.responses import FileResponse, JSONResponse -from fastapi.staticfiles import StaticFiles from crawler_platform.app.api.routes import register_routes from crawler_platform.app.core.database.session import init_db @@ -23,8 +22,18 @@ register_routes(app, DATABASE_URL) def _handle_key_error(_request: Request, exc: KeyError) -> JSONResponse: return JSONResponse(status_code=404, content={"detail": str(exc.args[0]) if exc.args else "not found"}) -if STATIC_DIR.exists(): - app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") +@app.get("/static/{asset_path:path}", include_in_schema=False) +def static_or_spa(asset_path: str): + static_root = STATIC_DIR.resolve() + target = (static_root / asset_path).resolve() + try: + target.relative_to(static_root) + except ValueError: + return JSONResponse(status_code=404, content={"detail": "not found"}) + + if target.is_file(): + return FileResponse(target) + return FileResponse(STATIC_DIR / "index.html") @app.get("/", include_in_schema=False) diff --git a/crawler_platform/app/web/frontend/src/App.tsx b/crawler_platform/app/web/frontend/src/App.tsx index da7afcd..51ff222 100644 --- a/crawler_platform/app/web/frontend/src/App.tsx +++ b/crawler_platform/app/web/frontend/src/App.tsx @@ -7,6 +7,12 @@ import ResearchPage from "@/pages/ResearchPage"; import OntologyEditorPage from "@/pages/OntologyEditorPage"; import ReviewPage from "@/pages/ReviewPage"; import DashboardPage from "@/pages/DashboardPage"; +import BuildPipelinePage from "@/pages/BuildPipelinePage"; +import PageAnalysisPage from "@/pages/PageAnalysisPage"; +import SchemaDesignerPage from "@/pages/SchemaDesignerPage"; +import QualityInspectorPage from "@/pages/QualityInspectorPage"; +import GraphViewPage from "@/pages/GraphViewPage"; +import ExportApiPage from "@/pages/ExportApiPage"; function App() { return ( @@ -16,9 +22,15 @@ function App() { } /> } /> } /> + } /> + } /> + } /> } /> } /> } /> + } /> + } /> + } /> ); diff --git a/crawler_platform/app/web/frontend/src/components/layout/AppShell.tsx b/crawler_platform/app/web/frontend/src/components/layout/AppShell.tsx index 33f1522..dec7d2a 100644 --- a/crawler_platform/app/web/frontend/src/components/layout/AppShell.tsx +++ b/crawler_platform/app/web/frontend/src/components/layout/AppShell.tsx @@ -14,9 +14,15 @@ import { Settings2, Activity, Brain, + Code2, + GitBranch, Network, ListChecks, Menu, + SearchCheck, + ShieldCheck, + ClipboardCheck, + Layers3, } from "lucide-react"; import { RootState } from "@/stores"; import { toggleSidebar } from "@/stores/slices/uiSlice"; @@ -28,25 +34,38 @@ interface NavItem { to?: string; projectPath?: string; labelKey: string; + label: string; icon: React.ComponentType<{ className?: string }>; } const navItems: NavItem[] = [ - { to: "/", labelKey: "nav.dashboard", icon: LayoutDashboard }, - { to: "/onboard", labelKey: "nav.onboard", icon: UploadCloud }, - { projectPath: "sources", labelKey: "nav.sources", icon: Settings2 }, - { projectPath: "crawl", labelKey: "nav.crawl", icon: Activity }, - { projectPath: "research", labelKey: "nav.research", icon: Brain }, - { projectPath: "editor", labelKey: "nav.editor", icon: Network }, - { projectPath: "review", labelKey: "nav.review", icon: ListChecks }, + { to: "/", labelKey: "nav.dashboard", label: "Dashboard", icon: LayoutDashboard }, + { to: "/onboard", labelKey: "nav.onboard", label: "New Project", icon: UploadCloud }, + { projectPath: "sources", labelKey: "nav.sources", label: "Source Explorer", icon: Settings2 }, + { projectPath: "crawl", labelKey: "nav.crawl", label: "Seed Crawl", icon: Activity }, + { projectPath: "pipeline", labelKey: "nav.pipeline", label: "Build Pipeline", icon: Layers3 }, + { projectPath: "analysis", labelKey: "nav.analysis", label: "Page Analysis", icon: SearchCheck }, + { projectPath: "schema", labelKey: "nav.schema", label: "Schema Designer", icon: ShieldCheck }, + { projectPath: "editor", labelKey: "nav.editor", label: "Entity Manager", icon: Network }, + { projectPath: "review", labelKey: "nav.review", label: "Claim Review", icon: ListChecks }, + { projectPath: "quality", labelKey: "nav.quality", label: "Quality Inspector", icon: ClipboardCheck }, + { projectPath: "graph", labelKey: "nav.graph", label: "Graph View", icon: GitBranch }, + { projectPath: "export", labelKey: "nav.export", label: "Export / API", icon: Code2 }, + { projectPath: "research", labelKey: "nav.research", label: "Graph Research", icon: Brain }, ]; const projectRoutePatterns = [ "/sources/:projectId", "/crawl/:projectId", + "/pipeline/:projectId", + "/analysis/:projectId", + "/schema/:projectId", "/research/:projectId", "/editor/:projectId", "/review/:projectId", + "/quality/:projectId", + "/graph/:projectId", + "/export/:projectId", ]; function projectIdFromPathname(pathname: string): string | undefined { @@ -137,7 +156,7 @@ export default function AppShell() { )}