aiecs 1.0.5__py3-none-any.whl → 1.0.6__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.
Potentially problematic release.
This version of aiecs might be problematic. Click here for more details.
- aiecs/__init__.py +1 -1
- aiecs/config/config.py +1 -0
- aiecs/domain/community/collaborative_workflow.py +407 -0
- aiecs/domain/community/community_integration.py +439 -0
- aiecs/domain/community/community_manager.py +337 -0
- aiecs/domain/community/decision_engine.py +354 -0
- aiecs/domain/community/models/community_models.py +204 -0
- aiecs/domain/community/resource_manager.py +438 -0
- aiecs/infrastructure/persistence/__init__.py +12 -0
- aiecs/infrastructure/persistence/context_engine_client.py +176 -0
- aiecs/llm/base_client.py +1 -0
- aiecs/llm/client_factory.py +4 -0
- aiecs/llm/googleai_client.py +165 -0
- aiecs/llm/vertex_client.py +71 -33
- aiecs/main.py +1 -1
- {aiecs-1.0.5.dist-info → aiecs-1.0.6.dist-info}/METADATA +2 -2
- {aiecs-1.0.5.dist-info → aiecs-1.0.6.dist-info}/RECORD +21 -13
- {aiecs-1.0.5.dist-info → aiecs-1.0.6.dist-info}/WHEEL +0 -0
- {aiecs-1.0.5.dist-info → aiecs-1.0.6.dist-info}/entry_points.txt +0 -0
- {aiecs-1.0.5.dist-info → aiecs-1.0.6.dist-info}/licenses/LICENSE +0 -0
- {aiecs-1.0.5.dist-info → aiecs-1.0.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Community Collaboration Models
|
|
3
|
+
|
|
4
|
+
Defines the data models for agent community collaboration,
|
|
5
|
+
including governance, resource sharing, and collective decision-making.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Dict, List, Any, Optional, Union
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from pydantic import BaseModel, Field, ConfigDict
|
|
12
|
+
import uuid
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CommunityRole(str, Enum):
|
|
16
|
+
"""Roles within the agent community."""
|
|
17
|
+
LEADER = "leader"
|
|
18
|
+
COORDINATOR = "coordinator"
|
|
19
|
+
SPECIALIST = "specialist"
|
|
20
|
+
CONTRIBUTOR = "contributor"
|
|
21
|
+
OBSERVER = "observer"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GovernanceType(str, Enum):
|
|
25
|
+
"""Types of community governance."""
|
|
26
|
+
DEMOCRATIC = "democratic" # Voting-based decisions
|
|
27
|
+
CONSENSUS = "consensus" # Consensus-based decisions
|
|
28
|
+
HIERARCHICAL = "hierarchical" # Leader-based decisions
|
|
29
|
+
HYBRID = "hybrid" # Mixed governance
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class DecisionStatus(str, Enum):
|
|
33
|
+
"""Status of community decisions."""
|
|
34
|
+
PROPOSED = "proposed"
|
|
35
|
+
VOTING = "voting"
|
|
36
|
+
APPROVED = "approved"
|
|
37
|
+
REJECTED = "rejected"
|
|
38
|
+
IMPLEMENTED = "implemented"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ResourceType(str, Enum):
|
|
42
|
+
"""Types of community resources."""
|
|
43
|
+
KNOWLEDGE = "knowledge"
|
|
44
|
+
TOOL = "tool"
|
|
45
|
+
EXPERIENCE = "experience"
|
|
46
|
+
DATA = "data"
|
|
47
|
+
CAPABILITY = "capability"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CommunityMember(BaseModel):
|
|
51
|
+
"""Model representing a member of the agent community."""
|
|
52
|
+
member_id: str = Field(..., description="Unique identifier for the member")
|
|
53
|
+
agent_id: str = Field(..., description="Associated agent ID")
|
|
54
|
+
agent_role: str = Field(..., description="Agent's functional role")
|
|
55
|
+
community_role: CommunityRole = Field(..., description="Role within the community")
|
|
56
|
+
|
|
57
|
+
# Participation metrics
|
|
58
|
+
contribution_score: float = Field(default=0.0, description="Contribution score to the community")
|
|
59
|
+
reputation: float = Field(default=0.0, description="Reputation within the community")
|
|
60
|
+
participation_level: str = Field(default="active", description="Level of participation")
|
|
61
|
+
|
|
62
|
+
# Capabilities and specializations
|
|
63
|
+
specializations: List[str] = Field(default_factory=list, description="Areas of specialization")
|
|
64
|
+
available_resources: List[str] = Field(default_factory=list, description="Resources this member can provide")
|
|
65
|
+
|
|
66
|
+
# Status
|
|
67
|
+
is_active: bool = Field(default=True, description="Whether the member is active")
|
|
68
|
+
joined_at: datetime = Field(default_factory=datetime.utcnow, description="When the member joined")
|
|
69
|
+
last_active_at: Optional[datetime] = Field(None, description="Last activity timestamp")
|
|
70
|
+
|
|
71
|
+
# Metadata
|
|
72
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
73
|
+
model_config = ConfigDict()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class CommunityResource(BaseModel):
|
|
77
|
+
"""Model representing a shared community resource."""
|
|
78
|
+
resource_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique resource identifier")
|
|
79
|
+
name: str = Field(..., description="Name of the resource")
|
|
80
|
+
resource_type: ResourceType = Field(..., description="Type of resource")
|
|
81
|
+
description: Optional[str] = Field(None, description="Description of the resource")
|
|
82
|
+
|
|
83
|
+
# Ownership and access
|
|
84
|
+
owner_id: str = Field(..., description="ID of the member who owns/contributed this resource")
|
|
85
|
+
access_level: str = Field(default="public", description="Access level (public, restricted, private)")
|
|
86
|
+
allowed_members: List[str] = Field(default_factory=list, description="Members allowed to access (if restricted)")
|
|
87
|
+
|
|
88
|
+
# Content
|
|
89
|
+
content: Dict[str, Any] = Field(default_factory=dict, description="Resource content/data")
|
|
90
|
+
tags: List[str] = Field(default_factory=list, description="Tags for categorization")
|
|
91
|
+
|
|
92
|
+
# Usage metrics
|
|
93
|
+
usage_count: int = Field(default=0, description="Number of times this resource has been used")
|
|
94
|
+
rating: float = Field(default=0.0, description="Average rating from community members")
|
|
95
|
+
|
|
96
|
+
# Status
|
|
97
|
+
is_available: bool = Field(default=True, description="Whether the resource is available")
|
|
98
|
+
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation timestamp")
|
|
99
|
+
updated_at: Optional[datetime] = Field(None, description="Last update timestamp")
|
|
100
|
+
|
|
101
|
+
# Metadata
|
|
102
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
103
|
+
model_config = ConfigDict()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class CommunityDecision(BaseModel):
|
|
107
|
+
"""Model representing a community decision or proposal."""
|
|
108
|
+
decision_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique decision identifier")
|
|
109
|
+
title: str = Field(..., description="Title of the decision/proposal")
|
|
110
|
+
description: str = Field(..., description="Detailed description")
|
|
111
|
+
|
|
112
|
+
# Proposal details
|
|
113
|
+
proposer_id: str = Field(..., description="ID of the member who proposed this")
|
|
114
|
+
decision_type: str = Field(..., description="Type of decision (policy, resource, task, etc.)")
|
|
115
|
+
priority: str = Field(default="medium", description="Priority level")
|
|
116
|
+
|
|
117
|
+
# Voting and consensus
|
|
118
|
+
status: DecisionStatus = Field(default=DecisionStatus.PROPOSED, description="Current status")
|
|
119
|
+
votes_for: List[str] = Field(default_factory=list, description="Member IDs who voted for")
|
|
120
|
+
votes_against: List[str] = Field(default_factory=list, description="Member IDs who voted against")
|
|
121
|
+
abstentions: List[str] = Field(default_factory=list, description="Member IDs who abstained")
|
|
122
|
+
|
|
123
|
+
# Implementation
|
|
124
|
+
implementation_plan: Optional[str] = Field(None, description="Plan for implementing the decision")
|
|
125
|
+
assigned_members: List[str] = Field(default_factory=list, description="Members assigned to implement")
|
|
126
|
+
deadline: Optional[datetime] = Field(None, description="Implementation deadline")
|
|
127
|
+
|
|
128
|
+
# Status tracking
|
|
129
|
+
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation timestamp")
|
|
130
|
+
voting_ends_at: Optional[datetime] = Field(None, description="When voting ends")
|
|
131
|
+
implemented_at: Optional[datetime] = Field(None, description="When decision was implemented")
|
|
132
|
+
|
|
133
|
+
# Metadata
|
|
134
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
135
|
+
model_config = ConfigDict()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class AgentCommunity(BaseModel):
|
|
139
|
+
"""Model representing an agent community."""
|
|
140
|
+
community_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique community identifier")
|
|
141
|
+
name: str = Field(..., description="Name of the community")
|
|
142
|
+
description: Optional[str] = Field(None, description="Description of the community")
|
|
143
|
+
|
|
144
|
+
# Governance
|
|
145
|
+
governance_type: GovernanceType = Field(default=GovernanceType.DEMOCRATIC, description="Type of governance")
|
|
146
|
+
governance_rules: Dict[str, Any] = Field(default_factory=dict, description="Governance rules and policies")
|
|
147
|
+
|
|
148
|
+
# Membership
|
|
149
|
+
members: List[str] = Field(default_factory=list, description="List of member IDs")
|
|
150
|
+
max_members: Optional[int] = Field(None, description="Maximum number of members")
|
|
151
|
+
membership_criteria: Dict[str, Any] = Field(default_factory=dict, description="Criteria for membership")
|
|
152
|
+
|
|
153
|
+
# Leadership
|
|
154
|
+
leaders: List[str] = Field(default_factory=list, description="List of leader member IDs")
|
|
155
|
+
coordinators: List[str] = Field(default_factory=list, description="List of coordinator member IDs")
|
|
156
|
+
|
|
157
|
+
# Resources and capabilities
|
|
158
|
+
shared_resources: List[str] = Field(default_factory=list, description="List of shared resource IDs")
|
|
159
|
+
collective_capabilities: List[str] = Field(default_factory=list, description="Collective capabilities")
|
|
160
|
+
knowledge_base: Dict[str, Any] = Field(default_factory=dict, description="Community knowledge base")
|
|
161
|
+
|
|
162
|
+
# Activity and metrics
|
|
163
|
+
activity_level: str = Field(default="active", description="Overall activity level")
|
|
164
|
+
collaboration_score: float = Field(default=0.0, description="Overall collaboration effectiveness score")
|
|
165
|
+
decision_count: int = Field(default=0, description="Number of decisions made")
|
|
166
|
+
resource_count: int = Field(default=0, description="Number of shared resources")
|
|
167
|
+
|
|
168
|
+
# Status
|
|
169
|
+
is_active: bool = Field(default=True, description="Whether the community is active")
|
|
170
|
+
created_at: datetime = Field(default_factory=datetime.utcnow, description="Creation timestamp")
|
|
171
|
+
updated_at: Optional[datetime] = Field(None, description="Last update timestamp")
|
|
172
|
+
|
|
173
|
+
# Metadata
|
|
174
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
175
|
+
model_config = ConfigDict()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class CollaborationSession(BaseModel):
|
|
179
|
+
"""Model representing a collaborative session between community members."""
|
|
180
|
+
session_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique session identifier")
|
|
181
|
+
community_id: str = Field(..., description="Associated community ID")
|
|
182
|
+
|
|
183
|
+
# Participants
|
|
184
|
+
participants: List[str] = Field(..., description="List of participating member IDs")
|
|
185
|
+
session_leader: Optional[str] = Field(None, description="Session leader member ID")
|
|
186
|
+
|
|
187
|
+
# Session details
|
|
188
|
+
purpose: str = Field(..., description="Purpose of the collaboration session")
|
|
189
|
+
session_type: str = Field(..., description="Type of session (brainstorm, decision, problem-solving, etc.)")
|
|
190
|
+
agenda: List[str] = Field(default_factory=list, description="Session agenda items")
|
|
191
|
+
|
|
192
|
+
# Outcomes
|
|
193
|
+
decisions_made: List[str] = Field(default_factory=list, description="Decision IDs made during session")
|
|
194
|
+
resources_created: List[str] = Field(default_factory=list, description="Resource IDs created during session")
|
|
195
|
+
action_items: List[Dict[str, Any]] = Field(default_factory=list, description="Action items from the session")
|
|
196
|
+
|
|
197
|
+
# Status
|
|
198
|
+
status: str = Field(default="active", description="Session status")
|
|
199
|
+
started_at: datetime = Field(default_factory=datetime.utcnow, description="Session start time")
|
|
200
|
+
ended_at: Optional[datetime] = Field(None, description="Session end time")
|
|
201
|
+
|
|
202
|
+
# Metadata
|
|
203
|
+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
|
|
204
|
+
model_config = ConfigDict()
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Community Resource Manager
|
|
3
|
+
|
|
4
|
+
Manages shared resources, knowledge bases, and collaborative tools
|
|
5
|
+
within agent communities.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Dict, List, Any, Optional, Set
|
|
11
|
+
import uuid
|
|
12
|
+
import json
|
|
13
|
+
|
|
14
|
+
from .models.community_models import (
|
|
15
|
+
CommunityResource, CommunityMember, AgentCommunity,
|
|
16
|
+
ResourceType
|
|
17
|
+
)
|
|
18
|
+
from ..core.exceptions.task_exceptions import TaskValidationError
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ResourceManager:
|
|
24
|
+
"""
|
|
25
|
+
Manager for community resources, knowledge sharing, and collaborative tools.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, community_manager=None, context_engine=None):
|
|
29
|
+
"""
|
|
30
|
+
Initialize the resource manager.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
community_manager: Reference to the community manager
|
|
34
|
+
context_engine: Optional context engine for persistent storage
|
|
35
|
+
"""
|
|
36
|
+
self.community_manager = community_manager
|
|
37
|
+
self.context_engine = context_engine
|
|
38
|
+
|
|
39
|
+
# Resource indexing and search
|
|
40
|
+
self.resource_index: Dict[str, Set[str]] = {} # tag -> set of resource_ids
|
|
41
|
+
self.type_index: Dict[ResourceType, Set[str]] = {} # type -> set of resource_ids
|
|
42
|
+
self.owner_index: Dict[str, Set[str]] = {} # owner_id -> set of resource_ids
|
|
43
|
+
|
|
44
|
+
# Knowledge graph for resource relationships
|
|
45
|
+
self.resource_relationships: Dict[str, Dict[str, List[str]]] = {}
|
|
46
|
+
|
|
47
|
+
# Usage analytics
|
|
48
|
+
self.usage_analytics: Dict[str, Dict[str, Any]] = {}
|
|
49
|
+
|
|
50
|
+
logger.info("Resource manager initialized")
|
|
51
|
+
|
|
52
|
+
async def create_knowledge_resource(
|
|
53
|
+
self,
|
|
54
|
+
community_id: str,
|
|
55
|
+
owner_member_id: str,
|
|
56
|
+
title: str,
|
|
57
|
+
content: str,
|
|
58
|
+
knowledge_type: str = "general",
|
|
59
|
+
tags: Optional[List[str]] = None,
|
|
60
|
+
related_resources: Optional[List[str]] = None
|
|
61
|
+
) -> str:
|
|
62
|
+
"""
|
|
63
|
+
Create a knowledge resource for the community.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
community_id: ID of the community
|
|
67
|
+
owner_member_id: ID of the member creating the resource
|
|
68
|
+
title: Title of the knowledge resource
|
|
69
|
+
content: Knowledge content
|
|
70
|
+
knowledge_type: Type of knowledge (general, expertise, experience, etc.)
|
|
71
|
+
tags: Tags for categorization
|
|
72
|
+
related_resources: IDs of related resources
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Resource ID
|
|
76
|
+
"""
|
|
77
|
+
resource_content = {
|
|
78
|
+
"title": title,
|
|
79
|
+
"content": content,
|
|
80
|
+
"knowledge_type": knowledge_type,
|
|
81
|
+
"created_at": datetime.utcnow().isoformat(),
|
|
82
|
+
"version": "1.0"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if related_resources:
|
|
86
|
+
resource_content["related_resources"] = related_resources
|
|
87
|
+
|
|
88
|
+
resource_id = await self.community_manager.create_community_resource(
|
|
89
|
+
community_id=community_id,
|
|
90
|
+
owner_member_id=owner_member_id,
|
|
91
|
+
name=title,
|
|
92
|
+
resource_type=ResourceType.KNOWLEDGE,
|
|
93
|
+
content=resource_content,
|
|
94
|
+
description=f"Knowledge resource: {knowledge_type}",
|
|
95
|
+
tags=tags or []
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Update indexes
|
|
99
|
+
await self._update_resource_indexes(resource_id, tags or [], ResourceType.KNOWLEDGE, owner_member_id)
|
|
100
|
+
|
|
101
|
+
# Create relationships
|
|
102
|
+
if related_resources:
|
|
103
|
+
await self._create_resource_relationships(resource_id, related_resources)
|
|
104
|
+
|
|
105
|
+
logger.info(f"Created knowledge resource: {title} ({resource_id})")
|
|
106
|
+
return resource_id
|
|
107
|
+
|
|
108
|
+
async def create_tool_resource(
|
|
109
|
+
self,
|
|
110
|
+
community_id: str,
|
|
111
|
+
owner_member_id: str,
|
|
112
|
+
tool_name: str,
|
|
113
|
+
tool_config: Dict[str, Any],
|
|
114
|
+
description: str,
|
|
115
|
+
usage_instructions: str,
|
|
116
|
+
tags: Optional[List[str]] = None
|
|
117
|
+
) -> str:
|
|
118
|
+
"""
|
|
119
|
+
Create a tool resource for community sharing.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
community_id: ID of the community
|
|
123
|
+
owner_member_id: ID of the member creating the resource
|
|
124
|
+
tool_name: Name of the tool
|
|
125
|
+
tool_config: Tool configuration
|
|
126
|
+
description: Tool description
|
|
127
|
+
usage_instructions: Instructions for using the tool
|
|
128
|
+
tags: Tags for categorization
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Resource ID
|
|
132
|
+
"""
|
|
133
|
+
resource_content = {
|
|
134
|
+
"tool_name": tool_name,
|
|
135
|
+
"tool_config": tool_config,
|
|
136
|
+
"description": description,
|
|
137
|
+
"usage_instructions": usage_instructions,
|
|
138
|
+
"created_at": datetime.utcnow().isoformat(),
|
|
139
|
+
"version": "1.0"
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
resource_id = await self.community_manager.create_community_resource(
|
|
143
|
+
community_id=community_id,
|
|
144
|
+
owner_member_id=owner_member_id,
|
|
145
|
+
name=tool_name,
|
|
146
|
+
resource_type=ResourceType.TOOL,
|
|
147
|
+
content=resource_content,
|
|
148
|
+
description=description,
|
|
149
|
+
tags=tags or []
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Update indexes
|
|
153
|
+
await self._update_resource_indexes(resource_id, tags or [], ResourceType.TOOL, owner_member_id)
|
|
154
|
+
|
|
155
|
+
logger.info(f"Created tool resource: {tool_name} ({resource_id})")
|
|
156
|
+
return resource_id
|
|
157
|
+
|
|
158
|
+
async def create_experience_resource(
|
|
159
|
+
self,
|
|
160
|
+
community_id: str,
|
|
161
|
+
owner_member_id: str,
|
|
162
|
+
experience_title: str,
|
|
163
|
+
situation: str,
|
|
164
|
+
actions_taken: List[str],
|
|
165
|
+
outcomes: Dict[str, Any],
|
|
166
|
+
lessons_learned: List[str],
|
|
167
|
+
tags: Optional[List[str]] = None
|
|
168
|
+
) -> str:
|
|
169
|
+
"""
|
|
170
|
+
Create an experience resource for knowledge sharing.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
community_id: ID of the community
|
|
174
|
+
owner_member_id: ID of the member sharing the experience
|
|
175
|
+
experience_title: Title of the experience
|
|
176
|
+
situation: Description of the situation
|
|
177
|
+
actions_taken: List of actions taken
|
|
178
|
+
outcomes: Outcomes and results
|
|
179
|
+
lessons_learned: Key lessons learned
|
|
180
|
+
tags: Tags for categorization
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Resource ID
|
|
184
|
+
"""
|
|
185
|
+
resource_content = {
|
|
186
|
+
"experience_title": experience_title,
|
|
187
|
+
"situation": situation,
|
|
188
|
+
"actions_taken": actions_taken,
|
|
189
|
+
"outcomes": outcomes,
|
|
190
|
+
"lessons_learned": lessons_learned,
|
|
191
|
+
"created_at": datetime.utcnow().isoformat(),
|
|
192
|
+
"experience_type": "case_study"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
resource_id = await self.community_manager.create_community_resource(
|
|
196
|
+
community_id=community_id,
|
|
197
|
+
owner_member_id=owner_member_id,
|
|
198
|
+
name=experience_title,
|
|
199
|
+
resource_type=ResourceType.EXPERIENCE,
|
|
200
|
+
content=resource_content,
|
|
201
|
+
description=f"Experience sharing: {situation[:100]}...",
|
|
202
|
+
tags=tags or []
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Update indexes
|
|
206
|
+
await self._update_resource_indexes(resource_id, tags or [], ResourceType.EXPERIENCE, owner_member_id)
|
|
207
|
+
|
|
208
|
+
logger.info(f"Created experience resource: {experience_title} ({resource_id})")
|
|
209
|
+
return resource_id
|
|
210
|
+
|
|
211
|
+
async def search_resources(
|
|
212
|
+
self,
|
|
213
|
+
community_id: str,
|
|
214
|
+
query: Optional[str] = None,
|
|
215
|
+
resource_type: Optional[ResourceType] = None,
|
|
216
|
+
tags: Optional[List[str]] = None,
|
|
217
|
+
owner_id: Optional[str] = None,
|
|
218
|
+
limit: int = 10
|
|
219
|
+
) -> List[Dict[str, Any]]:
|
|
220
|
+
"""
|
|
221
|
+
Search for community resources.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
community_id: ID of the community
|
|
225
|
+
query: Text query for searching
|
|
226
|
+
resource_type: Filter by resource type
|
|
227
|
+
tags: Filter by tags
|
|
228
|
+
owner_id: Filter by owner
|
|
229
|
+
limit: Maximum number of results
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
List of matching resources
|
|
233
|
+
"""
|
|
234
|
+
if not self.community_manager:
|
|
235
|
+
raise TaskValidationError("Community manager not available")
|
|
236
|
+
|
|
237
|
+
community = self.community_manager.communities.get(community_id)
|
|
238
|
+
if not community:
|
|
239
|
+
raise TaskValidationError(f"Community not found: {community_id}")
|
|
240
|
+
|
|
241
|
+
# Get candidate resource IDs
|
|
242
|
+
candidate_ids = set(community.shared_resources)
|
|
243
|
+
|
|
244
|
+
# Apply filters
|
|
245
|
+
if resource_type and resource_type in self.type_index:
|
|
246
|
+
candidate_ids &= self.type_index[resource_type]
|
|
247
|
+
|
|
248
|
+
if tags:
|
|
249
|
+
for tag in tags:
|
|
250
|
+
if tag in self.resource_index:
|
|
251
|
+
candidate_ids &= self.resource_index[tag]
|
|
252
|
+
|
|
253
|
+
if owner_id and owner_id in self.owner_index:
|
|
254
|
+
candidate_ids &= self.owner_index[owner_id]
|
|
255
|
+
|
|
256
|
+
# Get resource details and apply text search
|
|
257
|
+
results = []
|
|
258
|
+
for resource_id in candidate_ids:
|
|
259
|
+
resource = self.community_manager.resources.get(resource_id)
|
|
260
|
+
if not resource:
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
|
+
# Text search in resource content
|
|
264
|
+
if query:
|
|
265
|
+
searchable_text = f"{resource.name} {resource.description or ''} {json.dumps(resource.content)}"
|
|
266
|
+
if query.lower() not in searchable_text.lower():
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
# Add to results
|
|
270
|
+
results.append({
|
|
271
|
+
"resource_id": resource.resource_id,
|
|
272
|
+
"name": resource.name,
|
|
273
|
+
"resource_type": resource.resource_type,
|
|
274
|
+
"description": resource.description,
|
|
275
|
+
"owner_id": resource.owner_id,
|
|
276
|
+
"tags": resource.tags,
|
|
277
|
+
"usage_count": resource.usage_count,
|
|
278
|
+
"rating": resource.rating,
|
|
279
|
+
"created_at": resource.created_at,
|
|
280
|
+
"content_preview": self._get_content_preview(resource.content)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
if len(results) >= limit:
|
|
284
|
+
break
|
|
285
|
+
|
|
286
|
+
# Sort by relevance (usage count and rating)
|
|
287
|
+
results.sort(key=lambda x: (x["usage_count"], x["rating"]), reverse=True)
|
|
288
|
+
|
|
289
|
+
logger.info(f"Found {len(results)} resources for query: {query}")
|
|
290
|
+
return results
|
|
291
|
+
|
|
292
|
+
async def get_resource_recommendations(
|
|
293
|
+
self,
|
|
294
|
+
community_id: str,
|
|
295
|
+
member_id: str,
|
|
296
|
+
context: Optional[Dict[str, Any]] = None,
|
|
297
|
+
limit: int = 5
|
|
298
|
+
) -> List[Dict[str, Any]]:
|
|
299
|
+
"""
|
|
300
|
+
Get personalized resource recommendations for a member.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
community_id: ID of the community
|
|
304
|
+
member_id: ID of the member
|
|
305
|
+
context: Optional context for recommendations
|
|
306
|
+
limit: Maximum number of recommendations
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
List of recommended resources
|
|
310
|
+
"""
|
|
311
|
+
if not self.community_manager:
|
|
312
|
+
raise TaskValidationError("Community manager not available")
|
|
313
|
+
|
|
314
|
+
member = self.community_manager.members.get(member_id)
|
|
315
|
+
if not member:
|
|
316
|
+
raise TaskValidationError(f"Member not found: {member_id}")
|
|
317
|
+
|
|
318
|
+
# Get member's specializations and interests
|
|
319
|
+
member_tags = set(member.specializations)
|
|
320
|
+
|
|
321
|
+
# Find resources matching member's interests
|
|
322
|
+
candidate_resources = []
|
|
323
|
+
for tag in member_tags:
|
|
324
|
+
if tag in self.resource_index:
|
|
325
|
+
for resource_id in self.resource_index[tag]:
|
|
326
|
+
resource = self.community_manager.resources.get(resource_id)
|
|
327
|
+
if resource and resource.owner_id != member_id: # Don't recommend own resources
|
|
328
|
+
candidate_resources.append(resource)
|
|
329
|
+
|
|
330
|
+
# Score and rank resources
|
|
331
|
+
scored_resources = []
|
|
332
|
+
for resource in candidate_resources:
|
|
333
|
+
score = self._calculate_recommendation_score(resource, member, context)
|
|
334
|
+
scored_resources.append((score, resource))
|
|
335
|
+
|
|
336
|
+
# Sort by score and return top recommendations
|
|
337
|
+
scored_resources.sort(key=lambda x: x[0], reverse=True)
|
|
338
|
+
|
|
339
|
+
recommendations = []
|
|
340
|
+
for score, resource in scored_resources[:limit]:
|
|
341
|
+
recommendations.append({
|
|
342
|
+
"resource_id": resource.resource_id,
|
|
343
|
+
"name": resource.name,
|
|
344
|
+
"resource_type": resource.resource_type,
|
|
345
|
+
"description": resource.description,
|
|
346
|
+
"recommendation_score": score,
|
|
347
|
+
"tags": resource.tags,
|
|
348
|
+
"usage_count": resource.usage_count,
|
|
349
|
+
"rating": resource.rating
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
logger.info(f"Generated {len(recommendations)} recommendations for member {member_id}")
|
|
353
|
+
return recommendations
|
|
354
|
+
|
|
355
|
+
def _calculate_recommendation_score(
|
|
356
|
+
self,
|
|
357
|
+
resource: CommunityResource,
|
|
358
|
+
member: CommunityMember,
|
|
359
|
+
context: Optional[Dict[str, Any]] = None
|
|
360
|
+
) -> float:
|
|
361
|
+
"""Calculate recommendation score for a resource."""
|
|
362
|
+
score = 0.0
|
|
363
|
+
|
|
364
|
+
# Tag matching score
|
|
365
|
+
member_tags = set(member.specializations)
|
|
366
|
+
resource_tags = set(resource.tags)
|
|
367
|
+
tag_overlap = len(member_tags & resource_tags)
|
|
368
|
+
score += tag_overlap * 2.0
|
|
369
|
+
|
|
370
|
+
# Usage popularity score
|
|
371
|
+
score += resource.usage_count * 0.1
|
|
372
|
+
|
|
373
|
+
# Quality score
|
|
374
|
+
score += resource.rating * 1.0
|
|
375
|
+
|
|
376
|
+
# Recency score (newer resources get slight boost)
|
|
377
|
+
days_old = (datetime.utcnow() - resource.created_at).days
|
|
378
|
+
recency_score = max(0, 1.0 - (days_old / 365)) # Decay over a year
|
|
379
|
+
score += recency_score * 0.5
|
|
380
|
+
|
|
381
|
+
return score
|
|
382
|
+
|
|
383
|
+
async def _update_resource_indexes(
|
|
384
|
+
self,
|
|
385
|
+
resource_id: str,
|
|
386
|
+
tags: List[str],
|
|
387
|
+
resource_type: ResourceType,
|
|
388
|
+
owner_id: str
|
|
389
|
+
) -> None:
|
|
390
|
+
"""Update resource indexes for efficient searching."""
|
|
391
|
+
# Tag index
|
|
392
|
+
for tag in tags:
|
|
393
|
+
if tag not in self.resource_index:
|
|
394
|
+
self.resource_index[tag] = set()
|
|
395
|
+
self.resource_index[tag].add(resource_id)
|
|
396
|
+
|
|
397
|
+
# Type index
|
|
398
|
+
if resource_type not in self.type_index:
|
|
399
|
+
self.type_index[resource_type] = set()
|
|
400
|
+
self.type_index[resource_type].add(resource_id)
|
|
401
|
+
|
|
402
|
+
# Owner index
|
|
403
|
+
if owner_id not in self.owner_index:
|
|
404
|
+
self.owner_index[owner_id] = set()
|
|
405
|
+
self.owner_index[owner_id].add(resource_id)
|
|
406
|
+
|
|
407
|
+
async def _create_resource_relationships(
|
|
408
|
+
self,
|
|
409
|
+
resource_id: str,
|
|
410
|
+
related_resource_ids: List[str]
|
|
411
|
+
) -> None:
|
|
412
|
+
"""Create relationships between resources."""
|
|
413
|
+
if resource_id not in self.resource_relationships:
|
|
414
|
+
self.resource_relationships[resource_id] = {"related_to": [], "referenced_by": []}
|
|
415
|
+
|
|
416
|
+
for related_id in related_resource_ids:
|
|
417
|
+
# Add forward relationship
|
|
418
|
+
if related_id not in self.resource_relationships[resource_id]["related_to"]:
|
|
419
|
+
self.resource_relationships[resource_id]["related_to"].append(related_id)
|
|
420
|
+
|
|
421
|
+
# Add backward relationship
|
|
422
|
+
if related_id not in self.resource_relationships:
|
|
423
|
+
self.resource_relationships[related_id] = {"related_to": [], "referenced_by": []}
|
|
424
|
+
if resource_id not in self.resource_relationships[related_id]["referenced_by"]:
|
|
425
|
+
self.resource_relationships[related_id]["referenced_by"].append(resource_id)
|
|
426
|
+
|
|
427
|
+
def _get_content_preview(self, content: Dict[str, Any], max_length: int = 200) -> str:
|
|
428
|
+
"""Get a preview of resource content."""
|
|
429
|
+
if "content" in content:
|
|
430
|
+
text = str(content["content"])
|
|
431
|
+
elif "description" in content:
|
|
432
|
+
text = str(content["description"])
|
|
433
|
+
else:
|
|
434
|
+
text = str(content)
|
|
435
|
+
|
|
436
|
+
if len(text) > max_length:
|
|
437
|
+
return text[:max_length] + "..."
|
|
438
|
+
return text
|
|
@@ -5,8 +5,20 @@ Contains data persistence and storage infrastructure.
|
|
|
5
5
|
|
|
6
6
|
from .database_manager import DatabaseManager
|
|
7
7
|
from .redis_client import RedisClient
|
|
8
|
+
from .context_engine_client import (
|
|
9
|
+
initialize_context_engine,
|
|
10
|
+
get_context_engine,
|
|
11
|
+
close_context_engine,
|
|
12
|
+
is_context_engine_initialized,
|
|
13
|
+
reset_context_engine
|
|
14
|
+
)
|
|
8
15
|
|
|
9
16
|
__all__ = [
|
|
10
17
|
"DatabaseManager",
|
|
11
18
|
"RedisClient",
|
|
19
|
+
"initialize_context_engine",
|
|
20
|
+
"get_context_engine",
|
|
21
|
+
"close_context_engine",
|
|
22
|
+
"is_context_engine_initialized",
|
|
23
|
+
"reset_context_engine"
|
|
12
24
|
]
|