graph-dependency-analyzer 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. graph_dependency_analyzer-0.2.0.dist-info/METADATA +158 -0
  2. graph_dependency_analyzer-0.2.0.dist-info/RECORD +86 -0
  3. graph_dependency_analyzer-0.2.0.dist-info/WHEEL +5 -0
  4. graph_dependency_analyzer-0.2.0.dist-info/entry_points.txt +2 -0
  5. graph_dependency_analyzer-0.2.0.dist-info/top_level.txt +1 -0
  6. src/__init__.py +0 -0
  7. src/application/__init__.py +0 -0
  8. src/application/dtos/__init__.py +0 -0
  9. src/application/dtos/affected_component.py +59 -0
  10. src/application/dtos/batch_processing_result.py +17 -0
  11. src/application/dtos/impact_analysis_response.py +56 -0
  12. src/application/dtos/impact_query_request.py +38 -0
  13. src/application/dtos/index_batch_request.py +53 -0
  14. src/application/dtos/index_batch_response.py +72 -0
  15. src/application/dtos/index_result.py +20 -0
  16. src/application/dtos/repository_index_result.py +79 -0
  17. src/application/exceptions.py +41 -0
  18. src/application/services/__init__.py +0 -0
  19. src/application/services/file_scanner_service.py +258 -0
  20. src/application/services/progress_tracker.py +132 -0
  21. src/application/use_cases/__init__.py +0 -0
  22. src/application/use_cases/clear_all_data_use_case.py +180 -0
  23. src/application/use_cases/index_batch_use_case.py +153 -0
  24. src/application/use_cases/index_repository_use_case.py +683 -0
  25. src/application/use_cases/indexing_orchestrator.py +181 -0
  26. src/application/use_cases/list_repositories_use_case.py +84 -0
  27. src/cli.py +512 -0
  28. src/config/__init__.py +5 -0
  29. src/config/container.py +211 -0
  30. src/config/logging_config.py +123 -0
  31. src/config/settings.py +165 -0
  32. src/domain/__init__.py +0 -0
  33. src/domain/entities/__init__.py +20 -0
  34. src/domain/entities/ast_node.py +32 -0
  35. src/domain/entities/code_chunk.py +45 -0
  36. src/domain/entities/code_file.py +44 -0
  37. src/domain/entities/dependency_graph.py +110 -0
  38. src/domain/entities/repository.py +46 -0
  39. src/domain/exceptions.py +36 -0
  40. src/domain/repositories/__init__.py +22 -0
  41. src/domain/repositories/i_code_parser.py +50 -0
  42. src/domain/repositories/i_dependency_extractor.py +35 -0
  43. src/domain/repositories/i_embedding_provider.py +31 -0
  44. src/domain/repositories/i_graph_repository.py +78 -0
  45. src/domain/repositories/i_vector_repository.py +55 -0
  46. src/domain/services/__init__.py +8 -0
  47. src/domain/services/dependency_extractor.py +962 -0
  48. src/domain/services/semantic_chunk_builder.py +719 -0
  49. src/domain/value_objects/__init__.py +12 -0
  50. src/domain/value_objects/chunk_type.py +24 -0
  51. src/domain/value_objects/dependency_type.py +22 -0
  52. src/domain/value_objects/node_type.py +22 -0
  53. src/domain/value_objects/qualified_name.py +78 -0
  54. src/infrastructure/__init__.py +0 -0
  55. src/infrastructure/exceptions.py +41 -0
  56. src/infrastructure/external_apis/__init__.py +0 -0
  57. src/infrastructure/external_apis/ollama_embedding_provider.py +134 -0
  58. src/infrastructure/external_apis/openai_embedding_provider.py +231 -0
  59. src/infrastructure/parsing/__init__.py +20 -0
  60. src/infrastructure/parsing/config_file_parser.py +347 -0
  61. src/infrastructure/parsing/gradle_dependency_parser.py +159 -0
  62. src/infrastructure/parsing/groovy_parser.py +198 -0
  63. src/infrastructure/parsing/maven_dependency_parser.py +147 -0
  64. src/infrastructure/parsing/microfrontend_config_parser.py +242 -0
  65. src/infrastructure/parsing/npm_package_dependency_parser.py +255 -0
  66. src/infrastructure/parsing/python_parser.py +211 -0
  67. src/infrastructure/parsing/sql_schema_parser.py +658 -0
  68. src/infrastructure/parsing/tree_sitter_java_parser.py +434 -0
  69. src/infrastructure/parsing/tree_sitter_typescript_parser.py +600 -0
  70. src/infrastructure/persistence/__init__.py +0 -0
  71. src/infrastructure/persistence/graph_export_repository.py +443 -0
  72. src/infrastructure/persistence/neo4j_dependency_repository.py +657 -0
  73. src/infrastructure/persistence/qdrant_vector_repository.py +357 -0
  74. src/infrastructure/persistence/repository_relationship_repository.py +1312 -0
  75. src/presentation/__init__.py +0 -0
  76. src/presentation/api/__init__.py +0 -0
  77. src/presentation/api/rest/__init__.py +0 -0
  78. src/presentation/api/rest/exception_handlers.py +174 -0
  79. src/presentation/api/rest/main.py +80 -0
  80. src/presentation/api/rest/routers/__init__.py +5 -0
  81. src/presentation/api/rest/routers/admin.py +103 -0
  82. src/presentation/api/rest/routers/analysis.py +980 -0
  83. src/presentation/api/rest/routers/export.py +442 -0
  84. src/presentation/api/rest/routers/health.py +117 -0
  85. src/presentation/api/rest/routers/indexing.py +148 -0
  86. src/presentation/api/rest/routers/relationships.py +1089 -0
@@ -0,0 +1,211 @@
1
+ """
2
+ Container de injeção de dependências usando dependency-injector.
3
+
4
+ Requirements: 12.1, 12.2, 12.3, 12.4, 12.5
5
+ """
6
+ from functools import lru_cache
7
+
8
+ from dependency_injector import containers, providers
9
+
10
+ from src.config.settings import Settings
11
+
12
+
13
+ def _build_embedding_provider(config):
14
+ """
15
+ Factory that returns the correct embedding provider based on EMBEDDING_PROVIDER env var.
16
+
17
+ - "flow" or "openai" → OpenAIEmbeddingProvider (uses OPENAI_BASE_URL + OPENAI_API_KEY)
18
+ - "ollama" → OllamaEmbeddingProvider (uses OLLAMA_BASE_URL + OLLAMA_EMBEDDING_MODEL)
19
+ """
20
+ provider_type = getattr(config, "embedding_provider", "flow").lower()
21
+
22
+ if provider_type == "ollama":
23
+ from src.infrastructure.external_apis.ollama_embedding_provider import OllamaEmbeddingProvider
24
+ import structlog
25
+ structlog.get_logger(__name__).info(
26
+ "embedding_provider_selected",
27
+ provider="ollama",
28
+ model=getattr(config, "ollama_embedding_model", "nomic-embed-text"),
29
+ base_url=getattr(config, "ollama_base_url", "http://localhost:11434"),
30
+ )
31
+ return OllamaEmbeddingProvider(config=config)
32
+ else:
33
+ from src.infrastructure.external_apis.openai_embedding_provider import OpenAIEmbeddingProvider
34
+ import structlog
35
+ structlog.get_logger(__name__).info(
36
+ "embedding_provider_selected",
37
+ provider=provider_type,
38
+ model=getattr(config, "openai_embedding_model", "text-embedding-3-small"),
39
+ base_url=getattr(config, "openai_base_url", ""),
40
+ )
41
+ return OpenAIEmbeddingProvider(config=config)
42
+
43
+
44
+ class Container(containers.DeclarativeContainer):
45
+ """
46
+ Container central de injeção de dependências.
47
+
48
+ Registra todos os componentes da aplicação e gerencia seu ciclo de vida:
49
+ - Configurações (singleton)
50
+ - Componentes de infraestrutura (singletons)
51
+ - Use cases (factories)
52
+ """
53
+
54
+ # Configuração
55
+ config = providers.Singleton(Settings)
56
+
57
+ # === Application Services ===
58
+ file_scanner = providers.Factory(
59
+ "src.application.services.file_scanner_service.FileScannerService"
60
+ )
61
+
62
+ # === Infrastructure - Parsing ===
63
+ java_parser = providers.Singleton( # FIXED: Renamed from tree_sitter_parser (Task 7.1)
64
+ "src.infrastructure.parsing.tree_sitter_java_parser.TreeSitterJavaParser"
65
+ )
66
+
67
+ typescript_parser = providers.Singleton( # Task 12.1
68
+ "src.infrastructure.parsing.tree_sitter_typescript_parser.TreeSitterTypeScriptParser"
69
+ )
70
+
71
+ npm_parser = providers.Singleton( # Task 12.2
72
+ "src.infrastructure.parsing.npm_package_dependency_parser.NpmPackageDependencyParser"
73
+ )
74
+
75
+ microfrontend_parser = providers.Factory( # Task 12.3
76
+ "src.infrastructure.parsing.microfrontend_config_parser.MicrofrontendConfigParser",
77
+ typescript_parser=typescript_parser # Inject typescript_parser dependency
78
+ )
79
+
80
+ config_parser = providers.Singleton( # Task 12.4 (for DependencyExtractor)
81
+ "src.infrastructure.parsing.config_file_parser.ConfigFileParser"
82
+ )
83
+
84
+ # === Domain Services ===
85
+ dependency_extractor = providers.Factory( # Task 12.4
86
+ "src.domain.services.dependency_extractor.DependencyExtractor",
87
+ typescript_parser=typescript_parser,
88
+ npm_parser=npm_parser,
89
+ microfrontend_parser=microfrontend_parser,
90
+ config_parser=config_parser
91
+ )
92
+
93
+ chunk_builder = providers.Factory(
94
+ "src.domain.services.semantic_chunk_builder.SemanticChunkBuilder"
95
+ )
96
+
97
+ # === Infrastructure - External APIs ===
98
+ # Embedding provider is selected by EMBEDDING_PROVIDER env var:
99
+ # "flow" → OpenAIEmbeddingProvider with CI&T Flow proxy (default)
100
+ # "openai" → OpenAIEmbeddingProvider with OpenAI directly
101
+ # "ollama" → OllamaEmbeddingProvider with local Ollama instance
102
+ embedding_provider = providers.Singleton(
103
+ _build_embedding_provider,
104
+ config=config.provided.embedding,
105
+ )
106
+
107
+ # === Infrastructure - Clients ===
108
+ qdrant_client = providers.Singleton(
109
+ "qdrant_client.QdrantClient",
110
+ url=config.provided.qdrant_url
111
+ )
112
+
113
+ # Neo4j driver - usar Callable para chamar método estático
114
+ neo4j_driver = providers.Resource(
115
+ lambda uri, user, password: __import__('neo4j').AsyncGraphDatabase.driver(
116
+ uri, auth=(user, password)
117
+ ),
118
+ uri=config.provided.neo4j_uri,
119
+ user=config.provided.neo4j_user,
120
+ password=config.provided.neo4j_password
121
+ )
122
+
123
+ # === Infrastructure - Persistence ===
124
+ qdrant_repository = providers.Singleton(
125
+ "src.infrastructure.persistence.qdrant_vector_repository.QdrantVectorRepository",
126
+ client=qdrant_client,
127
+ collection_name=config.provided.qdrant_collection,
128
+ batch_size=providers.Object(1000),
129
+ vector_size=config.provided.qdrant_vector_size,
130
+ )
131
+
132
+ neo4j_repository = providers.Singleton(
133
+ "src.infrastructure.persistence.neo4j_dependency_repository.Neo4jDependencyGraphRepository",
134
+ driver=neo4j_driver,
135
+ batch_size=providers.Object(100)
136
+ )
137
+
138
+ sql_parser = providers.Singleton(
139
+ "src.infrastructure.parsing.sql_schema_parser.SqlSchemaParser"
140
+ )
141
+
142
+ python_parser = providers.Singleton(
143
+ "src.infrastructure.parsing.python_parser.PythonParser"
144
+ )
145
+
146
+ groovy_parser = providers.Singleton(
147
+ "src.infrastructure.parsing.groovy_parser.GroovyParser"
148
+ )
149
+
150
+ # === Use Cases ===
151
+ index_repository_use_case = providers.Factory(
152
+ "src.application.use_cases.index_repository_use_case.IndexRepositoryUseCase",
153
+ file_scanner=file_scanner,
154
+ java_parser=java_parser,
155
+ typescript_parser=typescript_parser,
156
+ dependency_extractor=dependency_extractor,
157
+ chunk_builder=chunk_builder,
158
+ embedding_provider=embedding_provider,
159
+ qdrant_repository=qdrant_repository,
160
+ neo4j_repository=neo4j_repository,
161
+ sql_parser=sql_parser,
162
+ python_parser=python_parser,
163
+ groovy_parser=groovy_parser,
164
+ )
165
+
166
+ indexing_orchestrator = providers.Factory(
167
+ "src.application.use_cases.indexing_orchestrator.IndexingOrchestrator",
168
+ index_repository_use_case=index_repository_use_case
169
+ )
170
+
171
+ index_batch_use_case = providers.Factory(
172
+ "src.application.use_cases.index_batch_use_case.IndexBatchUseCase",
173
+ orchestrator=indexing_orchestrator,
174
+ settings=config
175
+ )
176
+
177
+ list_repositories_use_case = providers.Factory(
178
+ "src.application.use_cases.list_repositories_use_case.ListRepositoriesUseCase",
179
+ graph_repository=neo4j_repository
180
+ )
181
+
182
+ clear_all_data_use_case = providers.Factory(
183
+ "src.application.use_cases.clear_all_data_use_case.ClearAllDataUseCase",
184
+ graph_repository=neo4j_repository,
185
+ qdrant_client=qdrant_client,
186
+ collection_name="code_chunks"
187
+ )
188
+
189
+ graph_export_repository = providers.Singleton(
190
+ "src.infrastructure.persistence.graph_export_repository.GraphExportRepository",
191
+ driver=neo4j_driver,
192
+ )
193
+
194
+ relationship_repository = providers.Singleton(
195
+ "src.infrastructure.persistence.repository_relationship_repository.RepositoryRelationshipRepository",
196
+ driver=neo4j_driver,
197
+ )
198
+
199
+
200
+ @lru_cache
201
+ def get_container() -> Container:
202
+ """
203
+ Retorna instância singleton do Container.
204
+
205
+ Usa @lru_cache para garantir que apenas uma instância do container
206
+ é criada e reutilizada em toda a aplicação.
207
+
208
+ Returns:
209
+ Container: Container de injeção de dependências
210
+ """
211
+ return Container()
@@ -0,0 +1,123 @@
1
+ """
2
+ Configuração de logging estruturado com structlog.
3
+
4
+ Requirements: 11.1, 11.2, 11.3, 11.4, 11.5
5
+ """
6
+ import logging
7
+ import sys
8
+ from typing import Any, Literal
9
+
10
+ import structlog
11
+ from structlog.types import EventDict, WrappedLogger
12
+
13
+
14
+ # Lista de campos sensíveis que devem ser sanitizados
15
+ SENSITIVE_FIELDS = {
16
+ "password",
17
+ "api_key",
18
+ "token",
19
+ "secret",
20
+ "authorization",
21
+ "auth",
22
+ "apikey",
23
+ "access_token",
24
+ "refresh_token",
25
+ }
26
+
27
+
28
+ def sanitize_event(
29
+ logger: WrappedLogger,
30
+ method_name: str,
31
+ event_dict: EventDict
32
+ ) -> EventDict:
33
+ """
34
+ Sanitiza dados sensíveis substituindo valores por ***REDACTED***.
35
+
36
+ Args:
37
+ logger: Logger do structlog
38
+ method_name: Nome do método sendo chamado
39
+ event_dict: Dicionário do evento de log
40
+
41
+ Returns:
42
+ EventDict com dados sensíveis sanitizados
43
+ """
44
+ for key in list(event_dict.keys()):
45
+ # Verificar se a chave é sensível (case-insensitive)
46
+ if key.lower() in SENSITIVE_FIELDS:
47
+ event_dict[key] = "***REDACTED***"
48
+
49
+ return event_dict
50
+
51
+
52
+ def configure_logging(
53
+ log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO"
54
+ ) -> None:
55
+ """
56
+ Configura o sistema de logging estruturado com structlog.
57
+
58
+ Configuração inclui:
59
+ - Formato JSON
60
+ - Timestamp ISO8601
61
+ - Nível de log
62
+ - Nome do logger
63
+ - Mensagem e contexto estruturado
64
+ - Sanitização de dados sensíveis
65
+
66
+ Args:
67
+ log_level: Nível de log (padrão: INFO)
68
+ """
69
+ # Mapear string para nível do logging
70
+ level_map = {
71
+ "DEBUG": logging.DEBUG,
72
+ "INFO": logging.INFO,
73
+ "WARNING": logging.WARNING,
74
+ "ERROR": logging.ERROR,
75
+ "CRITICAL": logging.CRITICAL,
76
+ }
77
+ numeric_level = level_map.get(log_level.upper(), logging.INFO)
78
+
79
+ # Configurar logging padrão do Python
80
+ logging.basicConfig(
81
+ format="%(message)s",
82
+ stream=sys.stdout,
83
+ level=numeric_level,
84
+ )
85
+
86
+ # Configurar structlog
87
+ structlog.configure(
88
+ processors=[
89
+ # Adicionar nível de log
90
+ structlog.stdlib.add_log_level,
91
+ # Adicionar nome do logger
92
+ structlog.stdlib.add_logger_name,
93
+ # Adicionar timestamp ISO8601
94
+ structlog.processors.TimeStamper(fmt="iso"),
95
+ # Sanitizar dados sensíveis
96
+ sanitize_event,
97
+ # Adicionar informações de exceção
98
+ structlog.processors.ExceptionRenderer(),
99
+ # Processar stack info
100
+ structlog.processors.StackInfoRenderer(),
101
+ # Formatar para dict
102
+ structlog.processors.dict_tracebacks,
103
+ # Renderizar como JSON
104
+ structlog.processors.JSONRenderer(),
105
+ ],
106
+ wrapper_class=structlog.stdlib.BoundLogger,
107
+ context_class=dict,
108
+ logger_factory=structlog.stdlib.LoggerFactory(),
109
+ cache_logger_on_first_use=True,
110
+ )
111
+
112
+
113
+ def get_logger(name: str) -> structlog.stdlib.BoundLogger:
114
+ """
115
+ Retorna um logger estruturado configurado.
116
+
117
+ Args:
118
+ name: Nome do logger (geralmente __name__ do módulo)
119
+
120
+ Returns:
121
+ Logger estruturado do structlog
122
+ """
123
+ return structlog.get_logger(name)
src/config/settings.py ADDED
@@ -0,0 +1,165 @@
1
+ """
2
+ Sistema de configuração centralizado usando Pydantic Settings.
3
+
4
+ Requirements: 10.1, 10.2, 10.3, 10.4, 10.5, 10.6
5
+ """
6
+ from functools import lru_cache
7
+ from pathlib import Path
8
+ from typing import Literal
9
+
10
+ from pydantic import Field
11
+ from pydantic_settings import BaseSettings, SettingsConfigDict
12
+
13
+ # Resolve .env relative to the project root (two levels up from this file)
14
+ _PROJECT_ROOT = Path(__file__).parent.parent.parent
15
+ _ENV_FILE = _PROJECT_ROOT / ".env"
16
+
17
+
18
+ class DatabaseConfig:
19
+ """Agrupamento de configurações Neo4j (namespace apenas)."""
20
+ pass
21
+
22
+
23
+ class VectorConfig:
24
+ """Agrupamento de configurações Qdrant (namespace apenas)."""
25
+ pass
26
+
27
+
28
+ class EmbeddingConfig:
29
+ """Agrupamento de configurações OpenAI (namespace apenas)."""
30
+ pass
31
+
32
+
33
+ class RepositoryConfig:
34
+ """Agrupamento de configurações de repositórios (namespace apenas)."""
35
+ pass
36
+
37
+
38
+ class Settings(BaseSettings):
39
+ """
40
+ Configurações centralizadas da aplicação.
41
+
42
+ Todas as configurações são carregadas de variáveis de ambiente via .env file.
43
+ """
44
+
45
+ # Configurações gerais
46
+ environment: Literal["development", "staging", "production"] = Field(
47
+ default="development",
48
+ description="Ambiente de execução"
49
+ )
50
+ log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = Field(
51
+ default="INFO",
52
+ description="Nível de log"
53
+ )
54
+ api_port: int = Field(default=8000, description="Porta da API")
55
+
56
+ # Neo4j Database Configuration
57
+ neo4j_uri: str = Field(..., description="URI de conexão Neo4j (ex: bolt://localhost:7687)")
58
+ neo4j_user: str = Field(..., description="Usuário Neo4j")
59
+ neo4j_password: str = Field(..., description="Senha Neo4j")
60
+ neo4j_pool_size: int = Field(default=50, description="Tamanho do pool de conexões")
61
+
62
+ # Qdrant Vector Database Configuration
63
+ qdrant_url: str = Field(..., description="URL do servidor Qdrant")
64
+ qdrant_collection: str = Field(default="code_chunks", description="Nome da collection")
65
+ qdrant_vector_size: int = Field(default=1536, description="Dimensão dos vetores")
66
+
67
+ # Embedding provider selection
68
+ # Options:
69
+ # "openai" — OpenAI API directly (default)
70
+ # "flow" — CI&T Flow proxy (uses OPENAI_BASE_URL + OPENAI_API_KEY)
71
+ # "ollama" — Local Ollama instance (free, no rate limits)
72
+ embedding_provider: Literal["openai", "flow", "ollama"] = Field(
73
+ default="flow",
74
+ description="Embedding provider: openai | flow | ollama"
75
+ )
76
+
77
+ # OpenAI / Flow Embeddings Configuration
78
+ openai_api_key: str = Field(default="", description="Chave API OpenAI ou Flow token")
79
+ openai_base_url: str = Field(
80
+ default="https://api.openai.com/v1",
81
+ description="URL base da API OpenAI ou Flow proxy"
82
+ )
83
+ openai_embedding_model: str = Field(
84
+ default="text-embedding-3-small",
85
+ description="Modelo de embedding OpenAI/Flow"
86
+ )
87
+
88
+ # Ollama Configuration (used when EMBEDDING_PROVIDER=ollama)
89
+ ollama_base_url: str = Field(
90
+ default="http://localhost:11434",
91
+ description="URL do servidor Ollama local"
92
+ )
93
+ ollama_embedding_model: str = Field(
94
+ default="nomic-embed-text",
95
+ description="Modelo de embedding Ollama (nomic-embed-text, mxbai-embed-large, all-minilm)"
96
+ )
97
+ # nomic-embed-text produces 768-dim vectors; mxbai-embed-large produces 1024-dim
98
+ # all-minilm produces 384-dim. Set QDRANT_VECTOR_SIZE to match.
99
+ ollama_embedding_dim: int = Field(
100
+ default=768,
101
+ description="Dimensão do vetor do modelo Ollama (768 para nomic-embed-text)"
102
+ )
103
+
104
+ # Repositories Configuration
105
+ repositories_path: str = Field(..., description="Caminho base para repositórios")
106
+
107
+ model_config = SettingsConfigDict(
108
+ env_file=str(_ENV_FILE),
109
+ env_file_encoding="utf-8",
110
+ case_sensitive=False,
111
+ extra="ignore",
112
+ )
113
+
114
+ @property
115
+ def database(self) -> DatabaseConfig:
116
+ """Acesso compatível ao namespace database."""
117
+ config = DatabaseConfig()
118
+ config.neo4j_uri = self.neo4j_uri
119
+ config.neo4j_user = self.neo4j_user
120
+ config.neo4j_password = self.neo4j_password
121
+ config.neo4j_pool_size = self.neo4j_pool_size
122
+ return config
123
+
124
+ @property
125
+ def vector(self) -> VectorConfig:
126
+ """Acesso compatível ao namespace vector."""
127
+ config = VectorConfig()
128
+ config.qdrant_url = self.qdrant_url
129
+ config.qdrant_collection = self.qdrant_collection
130
+ config.qdrant_vector_size = self.qdrant_vector_size
131
+ return config
132
+
133
+ @property
134
+ def embedding(self) -> EmbeddingConfig:
135
+ """Acesso compatível ao namespace embedding."""
136
+ config = EmbeddingConfig()
137
+ config.openai_api_key = self.openai_api_key
138
+ config.openai_base_url = self.openai_base_url
139
+ config.openai_embedding_model = self.openai_embedding_model
140
+ config.embedding_provider = self.embedding_provider
141
+ config.ollama_base_url = self.ollama_base_url
142
+ config.ollama_embedding_model = self.ollama_embedding_model
143
+ config.ollama_embedding_dim = self.ollama_embedding_dim
144
+ return config
145
+
146
+ @property
147
+ def repository(self) -> RepositoryConfig:
148
+ """Acesso compatível ao namespace repository."""
149
+ config = RepositoryConfig()
150
+ config.repositories_path = self.repositories_path
151
+ return config
152
+
153
+
154
+ @lru_cache
155
+ def get_settings() -> Settings:
156
+ """
157
+ Retorna instância singleton de Settings.
158
+
159
+ Usa @lru_cache para garantir que apenas uma instância é criada
160
+ e reutilizada em toda a aplicação.
161
+
162
+ Returns:
163
+ Settings: Configurações da aplicação
164
+ """
165
+ return Settings()
src/domain/__init__.py ADDED
File without changes
@@ -0,0 +1,20 @@
1
+ """Domain entities module."""
2
+ from src.domain.entities.repository import Repository
3
+ from src.domain.entities.code_file import CodeFile
4
+ from src.domain.entities.ast_node import ASTNode
5
+ from src.domain.entities.code_chunk import CodeChunk
6
+ from src.domain.entities.dependency_graph import (
7
+ DependencyGraph,
8
+ DependencyNode,
9
+ DependencyEdge,
10
+ )
11
+
12
+ __all__ = [
13
+ "Repository",
14
+ "CodeFile",
15
+ "ASTNode",
16
+ "CodeChunk",
17
+ "DependencyGraph",
18
+ "DependencyNode",
19
+ "DependencyEdge",
20
+ ]
@@ -0,0 +1,32 @@
1
+ """ASTNode entity - represents a parsed AST structure."""
2
+ from dataclasses import dataclass, field
3
+
4
+
5
+ @dataclass
6
+ class ASTNode:
7
+ """
8
+ ASTNode entity representing a parsed Abstract Syntax Tree node.
9
+
10
+ Attributes:
11
+ node_type: Type of node (CLASS, METHOD, IMPORT, etc.)
12
+ name: Simple name of the element
13
+ qualified_name: Fully qualified name (e.g., com.example.Class.method)
14
+ line_start: Starting line number in source file
15
+ line_end: Ending line number in source file
16
+ metadata: Additional metadata (parameters, return_type, modifiers, etc.)
17
+
18
+ Business Rules:
19
+ - line_end must be >= line_start
20
+ - qualified_name must be unique within repository
21
+ """
22
+ node_type: str
23
+ name: str
24
+ qualified_name: str
25
+ line_start: int
26
+ line_end: int
27
+ metadata: dict = field(default_factory=dict)
28
+
29
+ def __post_init__(self):
30
+ """Validate business rules after initialization."""
31
+ if self.line_end < self.line_start:
32
+ raise ValueError("line_end must be >= line_start")
@@ -0,0 +1,45 @@
1
+ """CodeChunk entity - represents a semantic code chunk for embeddings."""
2
+ from dataclasses import dataclass, field
3
+ from uuid import UUID, uuid4
4
+
5
+
6
+ @dataclass
7
+ class CodeChunk:
8
+ """
9
+ CodeChunk entity representing a semantic code chunk.
10
+
11
+ Chunks are method-based (not line-based) and include rich metadata
12
+ for embedding generation and retrieval.
13
+
14
+ Attributes:
15
+ id: Unique identifier (UUID v4)
16
+ content: Full source code of the chunk
17
+ chunk_type: Type of chunk (METHOD, CLASS, IMPORT_BLOCK, etc.)
18
+ metadata: Rich metadata (qualified_name, parameters, line_range, etc.)
19
+ embedding: Optional embedding vector (1536 dimensions)
20
+
21
+ Business Rules:
22
+ - Chunks never contain partial methods (always complete)
23
+ - Embedding must be exactly 1536 dimensions if set
24
+ - Each chunk has a unique ID
25
+ """
26
+ content: str
27
+ chunk_type: str
28
+ metadata: dict
29
+ id: UUID = field(default_factory=uuid4)
30
+ embedding: list[float] | None = None
31
+
32
+ def set_embedding(self, embedding: list[float]) -> None:
33
+ """
34
+ Set the embedding vector for this chunk.
35
+
36
+ Args:
37
+ embedding: 1536-dimensional embedding vector
38
+
39
+ Raises:
40
+ ValueError: If embedding is not 1536 dimensions
41
+ """
42
+ if len(embedding) != 1536:
43
+ raise ValueError("Embedding must have 1536 dimensions")
44
+
45
+ self.embedding = embedding
@@ -0,0 +1,44 @@
1
+ """CodeFile entity - represents a source code file."""
2
+ from dataclasses import dataclass, field
3
+ from pathlib import Path
4
+
5
+
6
+ @dataclass
7
+ class CodeFile:
8
+ """
9
+ CodeFile entity representing a single source code file.
10
+
11
+ Attributes:
12
+ file_path: Relative path to file within repository
13
+ language: Programming language (e.g., "java")
14
+ content: Full source code content
15
+ ast_nodes: List of parsed AST nodes
16
+ chunks: List of semantic code chunks
17
+
18
+ Business Rules:
19
+ - Each file belongs to exactly one repository
20
+ - File path must be relative to repository root
21
+ """
22
+ file_path: Path
23
+ language: str
24
+ content: str
25
+ ast_nodes: list = field(default_factory=list)
26
+ chunks: list = field(default_factory=list)
27
+
28
+ def add_ast_node(self, ast_node) -> None:
29
+ """
30
+ Add an AST node extracted from this file.
31
+
32
+ Args:
33
+ ast_node: ASTNode entity to add
34
+ """
35
+ self.ast_nodes.append(ast_node)
36
+
37
+ def add_chunk(self, chunk) -> None:
38
+ """
39
+ Add a semantic chunk created from this file.
40
+
41
+ Args:
42
+ chunk: CodeChunk entity to add
43
+ """
44
+ self.chunks.append(chunk)