lollms-client 1.3.7__py3-none-any.whl → 1.4.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.
Potentially problematic release.
This version of lollms-client might be problematic. Click here for more details.
- lollms_client/__init__.py +1 -1
- lollms_client/assets/models_ctx_sizes.json +40 -42
- lollms_client/lollms_core.py +2200 -110
- lollms_client/lollms_discussion.py +333 -75
- {lollms_client-1.3.7.dist-info → lollms_client-1.4.0.dist-info}/METADATA +4 -4
- {lollms_client-1.3.7.dist-info → lollms_client-1.4.0.dist-info}/RECORD +9 -9
- {lollms_client-1.3.7.dist-info → lollms_client-1.4.0.dist-info}/WHEEL +0 -0
- {lollms_client-1.3.7.dist-info → lollms_client-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {lollms_client-1.3.7.dist-info → lollms_client-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -134,7 +134,7 @@ def create_dynamic_models(
|
|
|
134
134
|
user_data_zone = Column(EncryptedText, nullable=True) # Field for persistent user-specific data
|
|
135
135
|
discussion_data_zone = Column(EncryptedText, nullable=True) # Field for persistent discussion-specific data
|
|
136
136
|
personality_data_zone = Column(EncryptedText, nullable=True) # Field for persistent personality-specific data
|
|
137
|
-
memory = Column(EncryptedText, nullable=True) #
|
|
137
|
+
memory = Column(EncryptedText, nullable=True) # Field for long-term memory, now managed with structured memories
|
|
138
138
|
|
|
139
139
|
participants = Column(JSON, nullable=True, default=dict)
|
|
140
140
|
active_branch_id = Column(String, nullable=True)
|
|
@@ -214,6 +214,60 @@ class LollmsDataManager:
|
|
|
214
214
|
self.SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=self.engine)
|
|
215
215
|
self.create_and_migrate_tables()
|
|
216
216
|
|
|
217
|
+
@staticmethod
|
|
218
|
+
def new_message(**kwargs) -> 'SimpleNamespace':
|
|
219
|
+
"""A static factory method to create a new message data object.
|
|
220
|
+
|
|
221
|
+
This is a convenience method for building message objects to be passed
|
|
222
|
+
to LollmsDiscussion.from_messages. It returns a SimpleNamespace that
|
|
223
|
+
mimics the structure of an ORM message object for in-memory use.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
**kwargs: Attributes for the new message (e.g., sender, content, sender_type).
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
A SimpleNamespace object representing the message data.
|
|
230
|
+
"""
|
|
231
|
+
# Set default sender based on sender_type if not provided
|
|
232
|
+
if 'sender' not in kwargs:
|
|
233
|
+
if kwargs.get('sender_type') == 'user':
|
|
234
|
+
kwargs['sender'] = 'user'
|
|
235
|
+
else:
|
|
236
|
+
kwargs['sender'] = 'assistant'
|
|
237
|
+
|
|
238
|
+
# Ensure default sender_type if not provided
|
|
239
|
+
if 'sender_type' not in kwargs:
|
|
240
|
+
if kwargs.get('sender') == 'user':
|
|
241
|
+
kwargs['sender_type'] = 'user'
|
|
242
|
+
else:
|
|
243
|
+
kwargs['sender_type'] = 'assistant'
|
|
244
|
+
|
|
245
|
+
# Default values for a new message
|
|
246
|
+
message_data = {
|
|
247
|
+
'id': str(uuid.uuid4()),
|
|
248
|
+
'parent_id': None, # Will be set by from_messages
|
|
249
|
+
'discussion_id': None, # Will be set by from_messages
|
|
250
|
+
'created_at': datetime.utcnow(),
|
|
251
|
+
'raw_content': kwargs.get('content'),
|
|
252
|
+
'thoughts': None,
|
|
253
|
+
'scratchpad': None,
|
|
254
|
+
'tokens': None,
|
|
255
|
+
'binding_name': None,
|
|
256
|
+
'model_name': None,
|
|
257
|
+
'generation_speed': None,
|
|
258
|
+
'message_metadata': {},
|
|
259
|
+
'images': [],
|
|
260
|
+
'active_images': [],
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
# Override defaults with user-provided kwargs
|
|
264
|
+
message_data.update(kwargs)
|
|
265
|
+
|
|
266
|
+
# Handle metadata alias
|
|
267
|
+
if 'metadata' in message_data:
|
|
268
|
+
message_data['message_metadata'] = message_data.pop('metadata')
|
|
269
|
+
|
|
270
|
+
return SimpleNamespace(**message_data)
|
|
217
271
|
def create_and_migrate_tables(self):
|
|
218
272
|
"""Creates all tables if they don't exist and performs simple schema migrations."""
|
|
219
273
|
self.Base.metadata.create_all(bind=self.engine)
|
|
@@ -516,6 +570,58 @@ class LollmsDiscussion:
|
|
|
516
570
|
self.get_discussion_images()
|
|
517
571
|
|
|
518
572
|
|
|
573
|
+
@classmethod
|
|
574
|
+
def from_messages(
|
|
575
|
+
cls,
|
|
576
|
+
messages: List[Any],
|
|
577
|
+
lollms_client: 'LollmsClient',
|
|
578
|
+
db_manager: Optional[LollmsDataManager] = None,
|
|
579
|
+
**kwargs
|
|
580
|
+
) -> 'LollmsDiscussion':
|
|
581
|
+
"""Creates a new discussion instance directly from a list of message objects.
|
|
582
|
+
|
|
583
|
+
This factory is useful for creating temporary or programmatic discussions
|
|
584
|
+
without manually adding each message. Messages are chained sequentially.
|
|
585
|
+
|
|
586
|
+
Args:
|
|
587
|
+
messages: A list of message-like objects (e.g., SimpleNamespace from
|
|
588
|
+
LollmsMessage.new_message).
|
|
589
|
+
lollms_client: The LollmsClient instance for the discussion.
|
|
590
|
+
db_manager: An optional LollmsDataManager to make the discussion persistent.
|
|
591
|
+
**kwargs: Additional arguments for the new discussion (e.g., system_prompt).
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
A new LollmsDiscussion instance populated with the provided messages.
|
|
595
|
+
"""
|
|
596
|
+
# Create a new, empty discussion
|
|
597
|
+
discussion = cls.create_new(
|
|
598
|
+
lollms_client=lollms_client,
|
|
599
|
+
db_manager=db_manager,
|
|
600
|
+
**kwargs
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
last_message_id = None
|
|
604
|
+
for msg_data in messages:
|
|
605
|
+
# Convert the message-like object to a dict for add_message
|
|
606
|
+
if isinstance(msg_data, SimpleNamespace):
|
|
607
|
+
msg_kwargs = msg_data.__dict__.copy()
|
|
608
|
+
elif isinstance(msg_data, dict):
|
|
609
|
+
msg_kwargs = msg_data.copy()
|
|
610
|
+
else:
|
|
611
|
+
raise TypeError("message objects must be of type dict or SimpleNamespace")
|
|
612
|
+
|
|
613
|
+
# Set the parent to the previous message in the list
|
|
614
|
+
msg_kwargs['parent_id'] = last_message_id
|
|
615
|
+
|
|
616
|
+
# Add the message and update the last_message_id for the next iteration
|
|
617
|
+
new_msg = discussion.add_message(**msg_kwargs)
|
|
618
|
+
last_message_id = new_msg.id
|
|
619
|
+
|
|
620
|
+
# The active_branch_id is already set to the last message by add_message,
|
|
621
|
+
# so no further action is needed.
|
|
622
|
+
|
|
623
|
+
return discussion
|
|
624
|
+
|
|
519
625
|
@classmethod
|
|
520
626
|
def create_new(cls, lollms_client: 'LollmsClient', db_manager: Optional[LollmsDataManager] = None, **kwargs) -> 'LollmsDiscussion':
|
|
521
627
|
"""Creates a new discussion and persists it if a db_manager is provided.
|
|
@@ -1540,71 +1646,81 @@ class LollmsDiscussion:
|
|
|
1540
1646
|
|
|
1541
1647
|
def memorize(self, branch_tip_id: Optional[str] = None):
|
|
1542
1648
|
"""
|
|
1543
|
-
Analyzes the current discussion
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
This is intended to build a persistent knowledge base about user preferences,
|
|
1547
|
-
facts, and context that can be useful across different future discussions.
|
|
1649
|
+
Analyzes the current discussion to create a structured memory of its essence,
|
|
1650
|
+
focusing on preserving detailed technical content, problems, and solutions.
|
|
1651
|
+
This new memory is then automatically saved and loaded into the context for immediate use.
|
|
1548
1652
|
|
|
1549
1653
|
Args:
|
|
1550
1654
|
branch_tip_id: The ID of the message to use as the end of the context
|
|
1551
|
-
|
|
1655
|
+
for memory extraction. Defaults to the active branch.
|
|
1552
1656
|
"""
|
|
1553
1657
|
try:
|
|
1554
|
-
# 1. Get the current conversation context
|
|
1555
1658
|
discussion_context = self.export("markdown", branch_tip_id=branch_tip_id)
|
|
1556
1659
|
if not discussion_context.strip():
|
|
1557
1660
|
print("[INFO] Memorize: Discussion is empty, nothing to memorize.")
|
|
1558
1661
|
return
|
|
1559
1662
|
|
|
1560
|
-
# 2. Formulate the prompt for the LLM
|
|
1561
1663
|
system_prompt = (
|
|
1562
|
-
"You are a
|
|
1563
|
-
"
|
|
1564
|
-
|
|
1565
|
-
"
|
|
1566
|
-
"-
|
|
1567
|
-
"-
|
|
1568
|
-
"-
|
|
1569
|
-
"
|
|
1570
|
-
"
|
|
1571
|
-
"
|
|
1664
|
+
"You are a Technical Knowledge Extraction AI specialized in preserving detailed information. "
|
|
1665
|
+
"Your task is to extract and preserve the ACTUAL CONTENT and DETAILS from discussions, not just summaries.\n\n"
|
|
1666
|
+
|
|
1667
|
+
"CRITICAL INSTRUCTIONS:\n"
|
|
1668
|
+
"- If equations, formulas, or code are mentioned, INCLUDE THE FULL EQUATIONS/FORMULAS/CODE in the memory\n"
|
|
1669
|
+
"- If technical procedures or steps are discussed, preserve the EXACT STEPS\n"
|
|
1670
|
+
"- If specific values, parameters, or constants are mentioned, include them\n"
|
|
1671
|
+
"- If problems and solutions are discussed, capture BOTH the problem statement AND the detailed solution\n"
|
|
1672
|
+
"- Focus on ACTIONABLE and REFERENCEABLE content that someone could use later\n"
|
|
1673
|
+
"- Preserve technical terminology, variable names, and specific implementation details\n"
|
|
1674
|
+
"- Do NOT create high-level summaries - capture the actual working content\n\n"
|
|
1675
|
+
|
|
1676
|
+
"OUTPUT FORMAT: JSON with 'title' (descriptive but specific) and 'content' (detailed technical content)"
|
|
1572
1677
|
)
|
|
1573
1678
|
|
|
1574
1679
|
prompt = (
|
|
1575
|
-
"
|
|
1576
|
-
|
|
1577
|
-
"
|
|
1680
|
+
"Extract the key technical content from this discussion. Focus on preserving:\n"
|
|
1681
|
+
"1. Complete equations, formulas, or code snippets\n"
|
|
1682
|
+
"2. Specific problem statements and their detailed solutions\n"
|
|
1683
|
+
"3. Step-by-step procedures or algorithms\n"
|
|
1684
|
+
"4. Important constants, values, or parameters\n"
|
|
1685
|
+
"5. Technical concepts with their precise definitions\n"
|
|
1686
|
+
"6. Any implementation details or configuration settings\n\n"
|
|
1687
|
+
|
|
1688
|
+
"IMPORTANT: Do not summarize what was discussed - extract the actual usable content.\n"
|
|
1689
|
+
"If Maxwell's equations were shown, include the actual equations.\n"
|
|
1690
|
+
"If code was provided, include the actual code.\n"
|
|
1691
|
+
"If a solution method was explained, include the actual steps.\n\n"
|
|
1692
|
+
|
|
1693
|
+
f"--- Conversation to Extract From ---\n{discussion_context}\n\n"
|
|
1694
|
+
|
|
1695
|
+
"Extract the technical essence that would be valuable for future reference:"
|
|
1578
1696
|
)
|
|
1579
1697
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
extracted_info = self.lollmsClient.generate_text(
|
|
1698
|
+
print("[INFO] Memorize: Extracting detailed technical content into a new memory...")
|
|
1699
|
+
memory_json = self.lollmsClient.generate_structured_content(
|
|
1583
1700
|
prompt,
|
|
1701
|
+
schema={
|
|
1702
|
+
"title": "A descriptive title indicating the type of problem solved (e.g., 'Python Import Error Fix', 'Database Connection Issue Solution')",
|
|
1703
|
+
"content": "Structured content with PROBLEM: [detailed problem] and SOLUTION: [detailed solution] sections"
|
|
1704
|
+
},
|
|
1584
1705
|
system_prompt=system_prompt,
|
|
1585
|
-
|
|
1586
|
-
temperature=0.1, # Low temperature for factual extraction
|
|
1587
|
-
top_k=10,
|
|
1706
|
+
temperature=0.1
|
|
1588
1707
|
)
|
|
1589
1708
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
self.memory = (current_memory + formatted_entry).strip()
|
|
1600
|
-
self.touch() # Mark as updated and save if autosave is on
|
|
1601
|
-
print(f"[INFO] Memorize: New information added to long-term memory.")
|
|
1709
|
+
if memory_json and memory_json.get("title") and memory_json.get("content"):
|
|
1710
|
+
title = memory_json["title"]
|
|
1711
|
+
self.add_memory(
|
|
1712
|
+
title=title,
|
|
1713
|
+
content=memory_json["content"]
|
|
1714
|
+
)
|
|
1715
|
+
# Automatically load the newly created memory into the context
|
|
1716
|
+
self.load_memory_into_context(title)
|
|
1717
|
+
print(f"[INFO] Memorize: New memory created and loaded into context: '{title}'.")
|
|
1602
1718
|
else:
|
|
1603
|
-
print("[
|
|
1719
|
+
print("[WARNING] Memorize: Failed to generate a valid memory from the discussion.")
|
|
1604
1720
|
|
|
1605
1721
|
except Exception as e:
|
|
1606
1722
|
trace_exception(e)
|
|
1607
|
-
print(f"[ERROR] Memorize: Failed to
|
|
1723
|
+
print(f"[ERROR] Memorize: Failed to create memory. {e}")
|
|
1608
1724
|
|
|
1609
1725
|
def count_discussion_tokens(self, format_type: str, branch_tip_id: Optional[str] = None) -> int:
|
|
1610
1726
|
"""Counts the number of tokens in the exported discussion content.
|
|
@@ -2341,6 +2457,180 @@ class LollmsDiscussion:
|
|
|
2341
2457
|
title=title, content=content, version=version, **extra_data
|
|
2342
2458
|
)
|
|
2343
2459
|
|
|
2460
|
+
def remove_artefact(self, title: str, version: Optional[int] = None) -> int:
|
|
2461
|
+
"""
|
|
2462
|
+
Removes artefacts by title. Removes all versions if `version` is None.
|
|
2463
|
+
|
|
2464
|
+
Returns:
|
|
2465
|
+
The number of artefact entries removed.
|
|
2466
|
+
"""
|
|
2467
|
+
new_metadata = (self.metadata or {}).copy()
|
|
2468
|
+
artefacts = new_metadata.get("_artefacts", [])
|
|
2469
|
+
if not artefacts:
|
|
2470
|
+
return 0
|
|
2471
|
+
|
|
2472
|
+
initial_count = len(artefacts)
|
|
2473
|
+
|
|
2474
|
+
if version is None:
|
|
2475
|
+
# Remove all versions with the matching title
|
|
2476
|
+
kept_artefacts = [a for a in artefacts if a.get('title') != title]
|
|
2477
|
+
else:
|
|
2478
|
+
# Remove only the specific title and version
|
|
2479
|
+
kept_artefacts = [a for a in artefacts if not (a.get('title') == title and a.get('version') == version)]
|
|
2480
|
+
|
|
2481
|
+
if len(kept_artefacts) < initial_count:
|
|
2482
|
+
new_metadata["_artefacts"] = kept_artefacts
|
|
2483
|
+
self.metadata = new_metadata
|
|
2484
|
+
self.commit()
|
|
2485
|
+
|
|
2486
|
+
removed_count = initial_count - len(kept_artefacts)
|
|
2487
|
+
if removed_count > 0:
|
|
2488
|
+
print(f"Removed {removed_count} artefact(s) titled '{title}'.")
|
|
2489
|
+
|
|
2490
|
+
return removed_count
|
|
2491
|
+
|
|
2492
|
+
# Memories management system
|
|
2493
|
+
def list_memories(self) -> List[Dict[str, Any]]:
|
|
2494
|
+
"""
|
|
2495
|
+
Lists all memories stored in the discussion's metadata.
|
|
2496
|
+
"""
|
|
2497
|
+
metadata = self.metadata or {}
|
|
2498
|
+
memories = metadata.get("_memories", [])
|
|
2499
|
+
now = datetime.utcnow().isoformat()
|
|
2500
|
+
|
|
2501
|
+
upgraded = []
|
|
2502
|
+
dirty = False
|
|
2503
|
+
for memory in memories:
|
|
2504
|
+
fixed = memory.copy()
|
|
2505
|
+
if "title" not in fixed: fixed["title"] = "untitled"; dirty = True
|
|
2506
|
+
if "content" not in fixed: fixed["content"] = ""; dirty = True
|
|
2507
|
+
if "created_at" not in fixed: fixed["created_at"] = now; dirty = True
|
|
2508
|
+
|
|
2509
|
+
section_start = f"--- Memory: {fixed['title']} ---"
|
|
2510
|
+
fixed["is_loaded"] = section_start in (self.memory or "")
|
|
2511
|
+
upgraded.append(fixed)
|
|
2512
|
+
|
|
2513
|
+
if dirty:
|
|
2514
|
+
metadata["_memories"] = upgraded
|
|
2515
|
+
self.metadata = metadata
|
|
2516
|
+
self.commit()
|
|
2517
|
+
|
|
2518
|
+
return upgraded
|
|
2519
|
+
|
|
2520
|
+
def add_memory(self, title: str, content: str, **extra_data) -> Dict[str, Any]:
|
|
2521
|
+
"""
|
|
2522
|
+
Adds or overwrites a memory in the discussion.
|
|
2523
|
+
"""
|
|
2524
|
+
new_metadata = (self.metadata or {}).copy()
|
|
2525
|
+
memories = new_metadata.get("_memories", [])
|
|
2526
|
+
|
|
2527
|
+
memories = [m for m in memories if m.get('title') != title]
|
|
2528
|
+
|
|
2529
|
+
new_memory = {
|
|
2530
|
+
"title": title, "content": content,
|
|
2531
|
+
"created_at": datetime.utcnow().isoformat(),
|
|
2532
|
+
**extra_data
|
|
2533
|
+
}
|
|
2534
|
+
memories.append(new_memory)
|
|
2535
|
+
|
|
2536
|
+
new_metadata["_memories"] = memories
|
|
2537
|
+
self.metadata = new_metadata
|
|
2538
|
+
self.commit()
|
|
2539
|
+
return new_memory
|
|
2540
|
+
|
|
2541
|
+
def get_memory(self, title: str) -> Optional[Dict[str, Any]]:
|
|
2542
|
+
"""
|
|
2543
|
+
Retrieves a memory by title.
|
|
2544
|
+
"""
|
|
2545
|
+
memories = self.list_memories()
|
|
2546
|
+
return next((m for m in memories if m.get('title') == title), None)
|
|
2547
|
+
|
|
2548
|
+
def load_memory_into_context(self, title: str):
|
|
2549
|
+
"""
|
|
2550
|
+
Loads a memory's content into the long-term memory context.
|
|
2551
|
+
"""
|
|
2552
|
+
memory = self.get_memory(title)
|
|
2553
|
+
if not memory:
|
|
2554
|
+
raise ValueError(f"Memory '{title}' not found.")
|
|
2555
|
+
|
|
2556
|
+
if memory.get('content'):
|
|
2557
|
+
section = (
|
|
2558
|
+
f"--- Memory: {memory['title']} ---\n"
|
|
2559
|
+
f"{memory['content']}\n"
|
|
2560
|
+
f"--- End Memory: {memory['title']} ---\n\n"
|
|
2561
|
+
)
|
|
2562
|
+
if section not in (self.memory or ""):
|
|
2563
|
+
current_memory_zone = self.memory or ""
|
|
2564
|
+
self.memory = current_memory_zone.rstrip() + "\n\n" + section
|
|
2565
|
+
self.touch()
|
|
2566
|
+
self.commit()
|
|
2567
|
+
print(f"Loaded memory '{title}' into context.")
|
|
2568
|
+
|
|
2569
|
+
def unload_memory_from_context(self, title: str):
|
|
2570
|
+
"""
|
|
2571
|
+
Removes a memory's content from the long-term memory context.
|
|
2572
|
+
"""
|
|
2573
|
+
memory = self.get_memory(title)
|
|
2574
|
+
if not memory:
|
|
2575
|
+
raise ValueError(f"Memory '{title}' not found.")
|
|
2576
|
+
|
|
2577
|
+
if self.memory and memory.get('content'):
|
|
2578
|
+
section_start = f"--- Memory: {memory['title']} ---"
|
|
2579
|
+
pattern = rf"\n*\s*{re.escape(section_start)}.*?--- End Memory: {re.escape(memory['title'])} ---\s*\n*"
|
|
2580
|
+
self.memory = re.sub(pattern, "", self.memory, flags=re.DOTALL).strip()
|
|
2581
|
+
self.touch()
|
|
2582
|
+
self.commit()
|
|
2583
|
+
print(f"Unloaded memory '{title}' from context.")
|
|
2584
|
+
|
|
2585
|
+
def is_memory_loaded(self, title: str) -> bool:
|
|
2586
|
+
"""
|
|
2587
|
+
Checks if a memory is currently loaded in the long-term memory context.
|
|
2588
|
+
"""
|
|
2589
|
+
memory = self.get_memory(title)
|
|
2590
|
+
if not memory:
|
|
2591
|
+
return False
|
|
2592
|
+
|
|
2593
|
+
section_start = f"--- Memory: {memory['title']} ---"
|
|
2594
|
+
return section_start in (self.memory or "")
|
|
2595
|
+
|
|
2596
|
+
def purge_memories(self) -> bool:
|
|
2597
|
+
"""
|
|
2598
|
+
Removes all memories from the discussion.
|
|
2599
|
+
|
|
2600
|
+
Returns:
|
|
2601
|
+
The number of memories removed (0 or 1).
|
|
2602
|
+
"""
|
|
2603
|
+
new_metadata = (self.metadata or {}).copy()
|
|
2604
|
+
new_metadata["_memories"] = []
|
|
2605
|
+
self.metadata = new_metadata
|
|
2606
|
+
self.commit()
|
|
2607
|
+
print(f"Removed memory titled.")
|
|
2608
|
+
return True
|
|
2609
|
+
|
|
2610
|
+
def remove_memory(self, title: str) -> int:
|
|
2611
|
+
"""
|
|
2612
|
+
Removes a memory by title.
|
|
2613
|
+
|
|
2614
|
+
Returns:
|
|
2615
|
+
The number of memories removed (0 or 1).
|
|
2616
|
+
"""
|
|
2617
|
+
new_metadata = (self.metadata or {}).copy()
|
|
2618
|
+
memories = new_metadata.get("_memories", [])
|
|
2619
|
+
if not memories:
|
|
2620
|
+
return 0
|
|
2621
|
+
|
|
2622
|
+
initial_count = len(memories)
|
|
2623
|
+
kept_memories = [m for m in memories if m.get('title') != title]
|
|
2624
|
+
|
|
2625
|
+
if len(kept_memories) < initial_count:
|
|
2626
|
+
new_metadata["_memories"] = kept_memories
|
|
2627
|
+
self.metadata = new_metadata
|
|
2628
|
+
self.commit()
|
|
2629
|
+
print(f"Removed memory titled '{title}'.")
|
|
2630
|
+
return 1
|
|
2631
|
+
|
|
2632
|
+
return 0
|
|
2633
|
+
|
|
2344
2634
|
def clone_without_messages(self) -> 'LollmsDiscussion':
|
|
2345
2635
|
"""
|
|
2346
2636
|
Creates a new discussion with the same context but no message history.
|
|
@@ -2433,36 +2723,4 @@ class LollmsDiscussion:
|
|
|
2433
2723
|
if db_manager:
|
|
2434
2724
|
new_discussion.commit()
|
|
2435
2725
|
|
|
2436
|
-
return new_discussion
|
|
2437
|
-
|
|
2438
|
-
def remove_artefact(self, title: str, version: Optional[int] = None) -> int:
|
|
2439
|
-
"""
|
|
2440
|
-
Removes artefacts by title. Removes all versions if `version` is None.
|
|
2441
|
-
|
|
2442
|
-
Returns:
|
|
2443
|
-
The number of artefact entries removed.
|
|
2444
|
-
"""
|
|
2445
|
-
new_metadata = (self.metadata or {}).copy()
|
|
2446
|
-
artefacts = new_metadata.get("_artefacts", [])
|
|
2447
|
-
if not artefacts:
|
|
2448
|
-
return 0
|
|
2449
|
-
|
|
2450
|
-
initial_count = len(artefacts)
|
|
2451
|
-
|
|
2452
|
-
if version is None:
|
|
2453
|
-
# Remove all versions with the matching title
|
|
2454
|
-
kept_artefacts = [a for a in artefacts if a.get('title') != title]
|
|
2455
|
-
else:
|
|
2456
|
-
# Remove only the specific title and version
|
|
2457
|
-
kept_artefacts = [a for a in artefacts if not (a.get('title') == title and a.get('version') == version)]
|
|
2458
|
-
|
|
2459
|
-
if len(kept_artefacts) < initial_count:
|
|
2460
|
-
new_metadata["_artefacts"] = kept_artefacts
|
|
2461
|
-
self.metadata = new_metadata
|
|
2462
|
-
self.commit()
|
|
2463
|
-
|
|
2464
|
-
removed_count = initial_count - len(kept_artefacts)
|
|
2465
|
-
if removed_count > 0:
|
|
2466
|
-
print(f"Removed {removed_count} artefact(s) titled '{title}'.")
|
|
2467
|
-
|
|
2468
|
-
return removed_count
|
|
2726
|
+
return new_discussion
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lollms_client
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: A client library for LoLLMs generate endpoint
|
|
5
5
|
Author-email: ParisNeo <parisneoai@gmail.com>
|
|
6
6
|
License: Apache Software License
|
|
@@ -56,7 +56,7 @@ Whether you're connecting to a remote LoLLMs server, an Ollama instance, the Ope
|
|
|
56
56
|
* 📝 **Advanced Structured Content Generation:** Reliably generate structured JSON output from natural language prompts using the `generate_structured_content` helper method, enforcing a specific schema.
|
|
57
57
|
* 💬 **Advanced Discussion Management:** Robustly manage conversation histories with `LollmsDiscussion`, featuring branching, context exporting, and automatic pruning.
|
|
58
58
|
* 🧠 **Persistent Memory & Data Zones:** `LollmsDiscussion` now supports multiple, distinct data zones (`user_data_zone`, `discussion_data_zone`, `personality_data_zone`) and a long-term `memory` field. This allows for sophisticated context layering and state management, enabling agents to learn and remember over time.
|
|
59
|
-
* ✍️ **
|
|
59
|
+
* ✍️ **Structured Memorization:** The `memorize()` method analyzes a conversation to extract its essence (e.g., a problem and its solution), creating a structured "memory" with a title and content. These memories are stored and can be explicitly loaded into the AI's context, providing a more robust and manageable long-term memory system.
|
|
60
60
|
* 📊 **Detailed Context Analysis:** The `get_context_status()` method provides a rich, detailed breakdown of the prompt context, showing the content and token count for each individual component (system prompt, data zones, message history).
|
|
61
61
|
* ⚙️ **Standardized Configuration Management:** A unified dictionary-based system (`llm_binding_config`) to configure any binding in a consistent manner.
|
|
62
62
|
* 🧩 **Extensible:** Designed to easily incorporate new LLM backends and modality services, including custom MCP toolsets.
|
|
@@ -384,7 +384,7 @@ with tempfile.TemporaryDirectory() as tmpdir:
|
|
|
384
384
|
# The 'breakdown' shows the individual zones that were combined
|
|
385
385
|
for name, content in sys_ctx.get('breakdown', {}).items():
|
|
386
386
|
# For brevity, show only first line of content
|
|
387
|
-
print(f" -> Contains '{name}': {content.split(os.linesep)
|
|
387
|
+
print(f" -> Contains '{name}': {content.split(os.linesep)}...")
|
|
388
388
|
|
|
389
389
|
# Print the message history details
|
|
390
390
|
if 'message_history' in status['zones']:
|
|
@@ -424,7 +424,7 @@ with tempfile.TemporaryDirectory() as tmpdir:
|
|
|
424
424
|
if name == 'memory':
|
|
425
425
|
ASCIIColors.yellow(f" -> Full '{name}' content:\n{content}")
|
|
426
426
|
else:
|
|
427
|
-
print(f" -> Contains '{name}': {content.split(os.linesep)
|
|
427
|
+
print(f" -> Contains '{name}': {content.split(os.linesep)}...")
|
|
428
428
|
print("------------------------------------------")
|
|
429
429
|
|
|
430
430
|
```
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
lollms_client/__init__.py,sha256=
|
|
1
|
+
lollms_client/__init__.py,sha256=qPZONrhBQLJOLMIGp9Z9tMt9xTQ-CulJRwCPSZwXaDY,1146
|
|
2
2
|
lollms_client/lollms_agentic.py,sha256=pQiMEuB_XkG29-SW6u4KTaMFPr6eKqacInggcCuCW3k,13914
|
|
3
3
|
lollms_client/lollms_config.py,sha256=goEseDwDxYJf3WkYJ4IrLXwg3Tfw73CXV2Avg45M_hE,21876
|
|
4
|
-
lollms_client/lollms_core.py,sha256=
|
|
5
|
-
lollms_client/lollms_discussion.py,sha256=
|
|
4
|
+
lollms_client/lollms_core.py,sha256=TSkwJN0mozPtCOiQQisqMUbZVVkqKKNdpjanOrKDjUM,289087
|
|
5
|
+
lollms_client/lollms_discussion.py,sha256=7O58DCvtd3zJ_W9wbuDOb5zcU6yt27xqSFkiQEH0wpk,127903
|
|
6
6
|
lollms_client/lollms_js_analyzer.py,sha256=01zUvuO2F_lnUe_0NLxe1MF5aHE1hO8RZi48mNPv-aw,8361
|
|
7
7
|
lollms_client/lollms_llm_binding.py,sha256=Dj1PI2bQBYv_JgPxCIaIC7DMUvWdFJGwXFdsP5hdGBg,25014
|
|
8
8
|
lollms_client/lollms_mcp_binding.py,sha256=psb27A23VFWDfZsR2WUbQXQxiZDW5yfOak6ZtbMfszI,10222
|
|
@@ -16,7 +16,7 @@ lollms_client/lollms_tts_binding.py,sha256=4qw94lc9M8lsh2q1u3FF0RuxTY__kukYg266a
|
|
|
16
16
|
lollms_client/lollms_ttv_binding.py,sha256=KkTaHLBhEEdt4sSVBlbwr5i_g_TlhcrwrT-7DjOsjWQ,4131
|
|
17
17
|
lollms_client/lollms_types.py,sha256=0iSH1QHRRD-ddBqoL9EEKJ8wWCuwDUlN_FrfbCdg7Lw,3522
|
|
18
18
|
lollms_client/lollms_utilities.py,sha256=3DAsII2X9uhRzRL-D0QlALcEdRg82y7OIL4yHVF32gY,19446
|
|
19
|
-
lollms_client/assets/models_ctx_sizes.json,sha256=
|
|
19
|
+
lollms_client/assets/models_ctx_sizes.json,sha256=jFDLW4GoT431544QXXyi9fA5tqIBmTrwaIA1_syoZ-Y,14972
|
|
20
20
|
lollms_client/llm_bindings/__init__.py,sha256=9sWGpmWSSj6KQ8H4lKGCjpLYwhnVdL_2N7gXCphPqh4,14
|
|
21
21
|
lollms_client/llm_bindings/azure_openai/__init__.py,sha256=XBDwct0nkvWfpo1J9J9lTOszH_c_4IiCYxEsG6aJLo0,16501
|
|
22
22
|
lollms_client/llm_bindings/claude/__init__.py,sha256=tzt9sR-9WlkgTgDBOtV708ZmuBjMm55fEYhurMnfXO4,24669
|
|
@@ -71,8 +71,8 @@ lollms_client/tts_bindings/xtts/server/main.py,sha256=T-Kn5NM-u1FJMygeV8rOoZKlqn
|
|
|
71
71
|
lollms_client/tts_bindings/xtts/server/setup_voices.py,sha256=UdHaPa5aNcw8dR-aRGkZr2OfSFFejH79lXgfwT0P3ss,1964
|
|
72
72
|
lollms_client/ttv_bindings/__init__.py,sha256=UZ8o2izQOJLQgtZ1D1cXoNST7rzqW22rL2Vufc7ddRc,3141
|
|
73
73
|
lollms_client/ttv_bindings/lollms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
74
|
-
lollms_client-1.
|
|
75
|
-
lollms_client-1.
|
|
76
|
-
lollms_client-1.
|
|
77
|
-
lollms_client-1.
|
|
78
|
-
lollms_client-1.
|
|
74
|
+
lollms_client-1.4.0.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
75
|
+
lollms_client-1.4.0.dist-info/METADATA,sha256=DWMUU9burfwT4loE8WrX5AUzw_TF4p282Ch7yIR-56I,58689
|
|
76
|
+
lollms_client-1.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
77
|
+
lollms_client-1.4.0.dist-info/top_level.txt,sha256=Bk_kz-ri6Arwsk7YG-T5VsRorV66uVhcHGvb_g2WqgE,14
|
|
78
|
+
lollms_client-1.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|