lollms-client 1.3.8__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.

@@ -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) # New field for long-term memory across discussions
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)
@@ -1646,71 +1646,81 @@ class LollmsDiscussion:
1646
1646
 
1647
1647
  def memorize(self, branch_tip_id: Optional[str] = None):
1648
1648
  """
1649
- Analyzes the current discussion, extracts key information suitable for long-term
1650
- memory, and appends it to the discussion's 'memory' field.
1651
-
1652
- This is intended to build a persistent knowledge base about user preferences,
1653
- 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.
1654
1652
 
1655
1653
  Args:
1656
1654
  branch_tip_id: The ID of the message to use as the end of the context
1657
- for memory extraction. Defaults to the active branch.
1655
+ for memory extraction. Defaults to the active branch.
1658
1656
  """
1659
1657
  try:
1660
- # 1. Get the current conversation context
1661
1658
  discussion_context = self.export("markdown", branch_tip_id=branch_tip_id)
1662
1659
  if not discussion_context.strip():
1663
1660
  print("[INFO] Memorize: Discussion is empty, nothing to memorize.")
1664
1661
  return
1665
1662
 
1666
- # 2. Formulate the prompt for the LLM
1667
1663
  system_prompt = (
1668
- "You are a Memory Extractor AI. Your task is to analyze a conversation "
1669
- "and extract only the most critical pieces of information that would be "
1670
- "valuable for a future, unrelated conversation with the same user. "
1671
- "Focus on: \n"
1672
- "- Explicit user preferences, goals, or facts about themselves.\n"
1673
- "- Key decisions or conclusions reached.\n"
1674
- "- Important entities, projects, or topics mentioned that are likely to recur.\n"
1675
- "Format the output as a concise list of bullet points. Be brief and factual. "
1676
- "Do not repeat information that is already in the User Data Zone or the Memory"
1677
- "If no new, significant long-term information is present, output the single word: 'NOTHING'."
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)"
1678
1677
  )
1679
1678
 
1680
1679
  prompt = (
1681
- "Analyze the following discussion and extract key information for long-term memory:\n\n"
1682
- f"--- Conversation ---\n{discussion_context}\n\n"
1683
- "--- Extracted Memory Points (as a bulleted list) ---"
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:"
1684
1696
  )
1685
1697
 
1686
- # 3. Call the LLM to extract information
1687
- print("[INFO] Memorize: Extracting key information from discussion...")
1688
- 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(
1689
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
+ },
1690
1705
  system_prompt=system_prompt,
1691
- n_predict=512, # A reasonable length for a summary
1692
- temperature=0.1, # Low temperature for factual extraction
1693
- top_k=10,
1706
+ temperature=0.1
1694
1707
  )
1695
1708
 
1696
- # 4. Process and append the information
1697
- if extracted_info and "NOTHING" not in extracted_info.upper():
1698
- new_memory_entry = extracted_info.strip()
1699
-
1700
- # Format with a timestamp for context
1701
- timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
1702
- formatted_entry = f"\n\n--- Memory entry from {timestamp} ---\n{new_memory_entry}"
1703
-
1704
- current_memory = self.memory or ""
1705
- self.memory = (current_memory + formatted_entry).strip()
1706
- self.touch() # Mark as updated and save if autosave is on
1707
- 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}'.")
1708
1718
  else:
1709
- print("[INFO] Memorize: No new significant information found to add to memory.")
1719
+ print("[WARNING] Memorize: Failed to generate a valid memory from the discussion.")
1710
1720
 
1711
1721
  except Exception as e:
1712
1722
  trace_exception(e)
1713
- print(f"[ERROR] Memorize: Failed to extract memory. {e}")
1723
+ print(f"[ERROR] Memorize: Failed to create memory. {e}")
1714
1724
 
1715
1725
  def count_discussion_tokens(self, format_type: str, branch_tip_id: Optional[str] = None) -> int:
1716
1726
  """Counts the number of tokens in the exported discussion content.
@@ -2447,6 +2457,180 @@ class LollmsDiscussion:
2447
2457
  title=title, content=content, version=version, **extra_data
2448
2458
  )
2449
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
+
2450
2634
  def clone_without_messages(self) -> 'LollmsDiscussion':
2451
2635
  """
2452
2636
  Creates a new discussion with the same context but no message history.
@@ -2539,36 +2723,4 @@ class LollmsDiscussion:
2539
2723
  if db_manager:
2540
2724
  new_discussion.commit()
2541
2725
 
2542
- return new_discussion
2543
-
2544
- def remove_artefact(self, title: str, version: Optional[int] = None) -> int:
2545
- """
2546
- Removes artefacts by title. Removes all versions if `version` is None.
2547
-
2548
- Returns:
2549
- The number of artefact entries removed.
2550
- """
2551
- new_metadata = (self.metadata or {}).copy()
2552
- artefacts = new_metadata.get("_artefacts", [])
2553
- if not artefacts:
2554
- return 0
2555
-
2556
- initial_count = len(artefacts)
2557
-
2558
- if version is None:
2559
- # Remove all versions with the matching title
2560
- kept_artefacts = [a for a in artefacts if a.get('title') != title]
2561
- else:
2562
- # Remove only the specific title and version
2563
- kept_artefacts = [a for a in artefacts if not (a.get('title') == title and a.get('version') == version)]
2564
-
2565
- if len(kept_artefacts) < initial_count:
2566
- new_metadata["_artefacts"] = kept_artefacts
2567
- self.metadata = new_metadata
2568
- self.commit()
2569
-
2570
- removed_count = initial_count - len(kept_artefacts)
2571
- if removed_count > 0:
2572
- print(f"Removed {removed_count} artefact(s) titled '{title}'.")
2573
-
2574
- 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.8
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
- * ✍️ **Automatic Memorization:** A new `memorize()` method allows the AI to analyze a conversation and extract key facts, appending them to the long-term `memory` for recall in future sessions.
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)[0]}...")
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)[0]}...")
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=Fk7jMpe7rlCAXA5rDjKP0dxSc5TCXoWX4UHtU_-Qo1U,1146
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=LS28j1Kh24NB80vJ4CI6HUsMjRp9I-8XCW7X8n8sNeQ,180837
5
- lollms_client/lollms_discussion.py,sha256=F92mJvpQXUFodsqEJVN54zSoJ3mJghFWwWqDOz-lGmY,121842
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
@@ -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.3.8.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
75
- lollms_client-1.3.8.dist-info/METADATA,sha256=6a5qX-Rj4rcNTiN3Yuw0HRbkBn8yET5Bh-F_4JB3RzU,58549
76
- lollms_client-1.3.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
77
- lollms_client-1.3.8.dist-info/top_level.txt,sha256=Bk_kz-ri6Arwsk7YG-T5VsRorV66uVhcHGvb_g2WqgE,14
78
- lollms_client-1.3.8.dist-info/RECORD,,
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,,