termux-aichain 1.0.0__tar.gz

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 (54) hide show
  1. termux_aichain-1.0.0/LICENSE +17 -0
  2. termux_aichain-1.0.0/PKG-INFO +31 -0
  3. termux_aichain-1.0.0/README.md +0 -0
  4. termux_aichain-1.0.0/pyproject.toml +43 -0
  5. termux_aichain-1.0.0/setup.cfg +4 -0
  6. termux_aichain-1.0.0/setup.py +24 -0
  7. termux_aichain-1.0.0/termux_aichain/__init__.py +149 -0
  8. termux_aichain-1.0.0/termux_aichain/cli.py +128 -0
  9. termux_aichain-1.0.0/termux_aichain/core/__init__.py +79 -0
  10. termux_aichain-1.0.0/termux_aichain/core/base.py +144 -0
  11. termux_aichain-1.0.0/termux_aichain/core/parsers.py +126 -0
  12. termux_aichain-1.0.0/termux_aichain/core/prompt.py +126 -0
  13. termux_aichain-1.0.0/termux_aichain/core/providers/__init__.py +23 -0
  14. termux_aichain-1.0.0/termux_aichain/core/providers/bitnet.py +31 -0
  15. termux_aichain-1.0.0/termux_aichain/core/providers/local_server.py +148 -0
  16. termux_aichain-1.0.0/termux_aichain/core/providers/openai_compatible.py +199 -0
  17. termux_aichain-1.0.0/termux_aichain/core/schema.py +103 -0
  18. termux_aichain-1.0.0/termux_aichain/core/splitters.py +236 -0
  19. termux_aichain-1.0.0/termux_aichain/device/__init__.py +29 -0
  20. termux_aichain-1.0.0/termux_aichain/device/ecosystem.py +97 -0
  21. termux_aichain-1.0.0/termux_aichain/device/tools.py +234 -0
  22. termux_aichain-1.0.0/termux_aichain/graph/__init__.py +27 -0
  23. termux_aichain-1.0.0/termux_aichain/graph/agent.py +118 -0
  24. termux_aichain-1.0.0/termux_aichain/graph/state.py +189 -0
  25. termux_aichain-1.0.0/termux_aichain/memory/__init__.py +16 -0
  26. termux_aichain-1.0.0/termux_aichain/memory/buffer.py +44 -0
  27. termux_aichain-1.0.0/termux_aichain/memory/extractor.py +39 -0
  28. termux_aichain-1.0.0/termux_aichain/memory/sqlite.py +159 -0
  29. termux_aichain-1.0.0/termux_aichain/serve/__init__.py +14 -0
  30. termux_aichain-1.0.0/termux_aichain/serve/dashboard.py +250 -0
  31. termux_aichain-1.0.0/termux_aichain/serve/server.py +223 -0
  32. termux_aichain-1.0.0/termux_aichain/trace/__init__.py +13 -0
  33. termux_aichain-1.0.0/termux_aichain/trace/tracer.py +142 -0
  34. termux_aichain-1.0.0/termux_aichain.egg-info/PKG-INFO +31 -0
  35. termux_aichain-1.0.0/termux_aichain.egg-info/SOURCES.txt +52 -0
  36. termux_aichain-1.0.0/termux_aichain.egg-info/dependency_links.txt +1 -0
  37. termux_aichain-1.0.0/termux_aichain.egg-info/entry_points.txt +2 -0
  38. termux_aichain-1.0.0/termux_aichain.egg-info/top_level.txt +1 -0
  39. termux_aichain-1.0.0/tests/test_core_bitnet.py +6 -0
  40. termux_aichain-1.0.0/tests/test_core_chain.py +42 -0
  41. termux_aichain-1.0.0/tests/test_core_parser.py +44 -0
  42. termux_aichain-1.0.0/tests/test_core_prompt.py +47 -0
  43. termux_aichain-1.0.0/tests/test_core_provider.py +103 -0
  44. termux_aichain-1.0.0/tests/test_core_splitter.py +41 -0
  45. termux_aichain-1.0.0/tests/test_dashboard.py +42 -0
  46. termux_aichain-1.0.0/tests/test_device.py +61 -0
  47. termux_aichain-1.0.0/tests/test_ecosystem.py +32 -0
  48. termux_aichain-1.0.0/tests/test_graph_agent.py +64 -0
  49. termux_aichain-1.0.0/tests/test_graph_state.py +109 -0
  50. termux_aichain-1.0.0/tests/test_local_server.py +76 -0
  51. termux_aichain-1.0.0/tests/test_memory.py +95 -0
  52. termux_aichain-1.0.0/tests/test_microscopic_edge_cases.py +145 -0
  53. termux_aichain-1.0.0/tests/test_serve.py +66 -0
  54. termux_aichain-1.0.0/tests/test_trace.py +58 -0
@@ -0,0 +1,17 @@
1
+  Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 UnoKim & AMEVA Open-Source Foundation
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,31 @@
1
+ Metadata-Version: 2.4
2
+ Name: termux-aichain
3
+ Version: 1.0.0
4
+ Summary: Sovereign Zero-Dependency AI Chaining and Multimodal Autonomous Agent Framework for Android Edge and Termux
5
+ Home-page: https://github.com/uno-km/termux-aichain
6
+ Author: UnoKim
7
+ Author-email: UnoKim <uno-km@users.noreply.github.com>
8
+ License: Apache-2.0
9
+ Project-URL: Homepage, https://github.com/uno-km/termux-aichain
10
+ Project-URL: Repository, https://github.com/uno-km/termux-aichain.git
11
+ Project-URL: Issues, https://github.com/uno-km/termux-aichain/issues
12
+ Keywords: termux,android,edge-ai,langchain,llama-cpp,bitnet,agent,zero-dependency
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Operating System :: POSIX :: Linux
22
+ Classifier: Operating System :: Android
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Dynamic: author
29
+ Dynamic: home-page
30
+ Dynamic: license-file
31
+ Dynamic: requires-python
File without changes
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "termux-aichain"
7
+ version = "1.0.0"
8
+ description = "Sovereign Zero-Dependency AI Chaining and Multimodal Autonomous Agent Framework for Android Edge and Termux"
9
+ readme = "README.md"
10
+ authors = [{ name = "UnoKim", email = "uno-km@users.noreply.github.com" }]
11
+ license = { text = "Apache-2.0" }
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: Apache Software License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Operating System :: POSIX :: Linux",
22
+ "Operating System :: Android",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
25
+ ]
26
+ keywords = ["termux", "android", "edge-ai", "langchain", "llama-cpp", "bitnet", "agent", "zero-dependency"]
27
+ requires-python = ">=3.10"
28
+ dependencies = []
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/uno-km/termux-aichain"
32
+ Repository = "https://github.com/uno-km/termux-aichain.git"
33
+ Issues = "https://github.com/uno-km/termux-aichain/issues"
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["."]
37
+ include = ["termux_aichain*"]
38
+
39
+ [tool.pytest.ini_options]
40
+ pythonpath = ["."]
41
+ testpaths = ["tests"]
42
+ [project.scripts]
43
+ termux-aichain = "termux_aichain.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,24 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="termux-aichain",
5
+ version="0.1.0",
6
+ description="Ultra-lightweight Zero-Dependency AI chaining & agent framework for Termux, Android and Edge computing.",
7
+ long_description=open("README.md", encoding="utf-8").read() if open("README.md", encoding="utf-8") else "",
8
+ long_description_content_type="text/markdown",
9
+ author="UnoKim",
10
+ author_email="uno-km@users.noreply.github.com",
11
+ url="https://github.com/uno-km/termux-aichain",
12
+ packages=find_packages(),
13
+ python_requires=">=3.10",
14
+ install_requires=[],
15
+ classifiers=[
16
+ "License :: OSI Approved :: Apache Software License",
17
+ "Operating System :: Android",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ ],
24
+ )
@@ -0,0 +1,149 @@
1
+ """
2
+ ==============================================================================
3
+ termux-aichain: Sovereign Zero-Dependency Edge AI Framework for Termux & Android
4
+ ==============================================================================
5
+ Copyright (c) 2026 AMEVA Open-Source Foundation & UnoKim.
6
+ Licensed under the Apache License, Version 2.0.
7
+ """
8
+
9
+ __version__ = "1.0.0"
10
+ __author__ = "UnoKim <uno-km@users.noreply.github.com>"
11
+
12
+ from termux_aichain.core.schema import (
13
+ Message,
14
+ HumanMessage,
15
+ AIMessage,
16
+ SystemMessage,
17
+ ToolMessage,
18
+ UsageInfo,
19
+ GenerationResult,
20
+ StreamChunk,
21
+ )
22
+ from termux_aichain.core.prompt import (
23
+ PromptTemplate,
24
+ ChatPromptTemplate,
25
+ )
26
+ from termux_aichain.core.base import (
27
+ Runnable,
28
+ RunnableLambda,
29
+ RunnableSequence,
30
+ BaseChatModel,
31
+ )
32
+ from termux_aichain.core.providers.openai_compatible import OpenAICompatibleChat
33
+ from termux_aichain.core.providers.bitnet import BitNetChat
34
+ from termux_aichain.core.providers.local_server import (
35
+ LocalServerConfig,
36
+ LocalServerManager,
37
+ LlamaCppServer,
38
+ BitNetServer,
39
+ )
40
+ from termux_aichain.core.parsers import (
41
+ StringOutputParser,
42
+ JsonOutputParser,
43
+ RegexOutputParser,
44
+ )
45
+ from termux_aichain.core.splitters import (
46
+ Document,
47
+ CharacterTextSplitter,
48
+ RecursiveCharacterTextSplitter,
49
+ TextLoader,
50
+ MarkdownLoader,
51
+ JSONLoader,
52
+ )
53
+ from termux_aichain.graph.state import (
54
+ StateGraph,
55
+ CompiledGraph,
56
+ START,
57
+ END,
58
+ )
59
+ from termux_aichain.graph.agent import (
60
+ Tool,
61
+ tool,
62
+ create_react_agent,
63
+ )
64
+ from termux_aichain.memory.buffer import ConversationBufferMemory
65
+ from termux_aichain.memory.sqlite import SQLiteEntityMemory, SQLiteVectorStore
66
+ from termux_aichain.memory.extractor import FactExtractor
67
+ from termux_aichain.serve.server import AgentServer, serve
68
+ from termux_aichain.serve.dashboard import DASHBOARD_HTML
69
+ from termux_aichain.trace.tracer import TraceSpan, Tracer, traceable
70
+ from termux_aichain.device.tools import (
71
+ get_battery_status,
72
+ get_sensor_data,
73
+ get_device_location,
74
+ record_speech_to_text,
75
+ vibrate_device,
76
+ send_notification,
77
+ speak_tts,
78
+ execute_shell,
79
+ get_default_device_tools,
80
+ )
81
+ from termux_aichain.device.ecosystem import (
82
+ transcribe_speech,
83
+ generate_diffusion_image,
84
+ browse_web_headless,
85
+ get_ecosystem_tools,
86
+ )
87
+
88
+ __all__ = [
89
+ "__version__",
90
+ "Message",
91
+ "HumanMessage",
92
+ "AIMessage",
93
+ "SystemMessage",
94
+ "ToolMessage",
95
+ "UsageInfo",
96
+ "GenerationResult",
97
+ "StreamChunk",
98
+ "PromptTemplate",
99
+ "ChatPromptTemplate",
100
+ "Runnable",
101
+ "RunnableLambda",
102
+ "RunnableSequence",
103
+ "BaseChatModel",
104
+ "OpenAICompatibleChat",
105
+ "BitNetChat",
106
+ "LocalServerConfig",
107
+ "LocalServerManager",
108
+ "LlamaCppServer",
109
+ "BitNetServer",
110
+ "StringOutputParser",
111
+ "JsonOutputParser",
112
+ "RegexOutputParser",
113
+ "Document",
114
+ "CharacterTextSplitter",
115
+ "RecursiveCharacterTextSplitter",
116
+ "TextLoader",
117
+ "MarkdownLoader",
118
+ "JSONLoader",
119
+ "StateGraph",
120
+ "CompiledGraph",
121
+ "START",
122
+ "END",
123
+ "Tool",
124
+ "tool",
125
+ "create_react_agent",
126
+ "ConversationBufferMemory",
127
+ "SQLiteEntityMemory",
128
+ "SQLiteVectorStore",
129
+ "FactExtractor",
130
+ "AgentServer",
131
+ "serve",
132
+ "DASHBOARD_HTML",
133
+ "TraceSpan",
134
+ "Tracer",
135
+ "traceable",
136
+ "get_battery_status",
137
+ "get_sensor_data",
138
+ "get_device_location",
139
+ "record_speech_to_text",
140
+ "vibrate_device",
141
+ "send_notification",
142
+ "speak_tts",
143
+ "execute_shell",
144
+ "get_default_device_tools",
145
+ "transcribe_speech",
146
+ "generate_diffusion_image",
147
+ "browse_web_headless",
148
+ "get_ecosystem_tools",
149
+ ]
@@ -0,0 +1,128 @@
1
+ """
2
+ ==============================================================================
3
+ termux-aichain Unified Command Line Interface & One-Touch Provisioner
4
+ ==============================================================================
5
+ Provides sovereign zero-state setup, environment diagnostics, model pull,
6
+ and 1-line serving CLI for Android Termux.
7
+ Zero external heavy dependencies - Pure Python 3.10+ standard library.
8
+ """
9
+
10
+ from __future__ import annotations
11
+ import os
12
+ import sys
13
+ import shutil
14
+ import argparse
15
+ import subprocess
16
+ import urllib.request
17
+ from termux_aichain import __version__, serve, PromptTemplate, LocalServerConfig, LlamaCppServer
18
+
19
+ MODELS_REGISTRY = {
20
+ "llama-3.2-3b": {
21
+ "url": "https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_K_M.gguf",
22
+ "filename": "Llama-3.2-3B-Instruct-Q4_K_M.gguf",
23
+ "size_desc": "~1.9 GB",
24
+ },
25
+ "qwen-2.5-1.5b": {
26
+ "url": "https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF/resolve/main/qwen2.5-1.5b-instruct-q4_k_m.gguf",
27
+ "filename": "qwen2.5-1.5b-instruct-q4_k_m.gguf",
28
+ "size_desc": "~0.98 GB",
29
+ },
30
+ "bitnet-3b": {
31
+ "url": "https://huggingface.co/1bitLLM/bitnet_b1_58-3B-GGUF/resolve/main/bitnet_b1_58-3B-Q4_K_M.gguf",
32
+ "filename": "bitnet_b1_58-3B-Q4_K_M.gguf",
33
+ "size_desc": "~1.8 GB",
34
+ }
35
+ }
36
+
37
+ def cmd_setup() -> None:
38
+ """Diagnoses environment and provisions required native tools."""
39
+ print("=" * 70)
40
+ print(f"⚡ termux-aichain v{__version__} One-Touch Environment Diagnostics")
41
+ print("=" * 70)
42
+
43
+ is_termux = "com.termux" in os.environ.get("PREFIX", "") or os.path.exists("/data/data/com.termux")
44
+ print(f"• Platform Environment : {'Android Termux (Native)' if is_termux else 'Host OS'}")
45
+ print(f"• Python Version : {sys.version.split()[0]}")
46
+
47
+ # Check Termux:API
48
+ has_api = bool(shutil.which("termux-battery-status"))
49
+ print(f"• Termux-API CLI Tools : {'[OK] Installed' if has_api else '[WARN] Not Installed (Using Kernel Sysfs Fallback)'}")
50
+
51
+ # Check llama-server
52
+ has_llama = bool(shutil.which("llama-server"))
53
+ print(f"• Local llama-server : {'[OK] Available' if has_llama else '[OPTIONAL] Not in PATH (Can use external/bitnet endpoint)'}")
54
+
55
+ # Check Node.js
56
+ has_node = bool(shutil.which("node"))
57
+ print(f"• Node.js ESM Runtime : {'[OK] ' + subprocess.check_output(['node', '-v'], text=True).strip() if has_node else '[INFO] Node.js not detected'}")
58
+
59
+ print("-" * 70)
60
+ if is_termux and not has_api:
61
+ print("[*] To enable full hardware sensors/vibration on Termux, run:")
62
+ print(" pkg update && pkg install termux-api -y\n")
63
+ print("[+] All Core, Graph, Memory, Serve, and Trace modules are 100% ready!")
64
+ print("=" * 70)
65
+
66
+ def cmd_pull(model_name: str) -> None:
67
+ """Downloads validated lightweight model GGUF for local inference."""
68
+ target = model_name.lower().strip()
69
+ if target not in MODELS_REGISTRY:
70
+ print(f"[-] Unknown model '{model_name}'. Available options:")
71
+ for k, v in MODELS_REGISTRY.items():
72
+ print(f" - {k} ({v['size_desc']})")
73
+ return
74
+
75
+ info = MODELS_REGISTRY[target]
76
+ dest_dir = os.path.expanduser("~/models")
77
+ os.makedirs(dest_dir, exist_ok=True)
78
+ dest_file = os.path.join(dest_dir, info["filename"])
79
+
80
+ if os.path.exists(dest_file):
81
+ print(f"[*] Model already exists at: {dest_file}")
82
+ return
83
+
84
+ print(f"[*] Downloading {target} ({info['size_desc']}) to {dest_file}...")
85
+ try:
86
+ urllib.request.urlretrieve(info["url"], dest_file)
87
+ print(f"[+] Successfully downloaded: {dest_file}")
88
+ except Exception as ex:
89
+ print(f"[-] Download failed: {str(ex)}")
90
+
91
+ def cmd_serve(port: int, host: str) -> None:
92
+ """Launches instant 1-line agent server with Live Web Dashboard."""
93
+ prompt = PromptTemplate.from_template("Edge Task: {input}")
94
+ chain = prompt | (lambda s: f"termux-aichain processed: {s}")
95
+ serve(chain, host=host, port=port, block=True)
96
+
97
+ def main() -> None:
98
+ parser = argparse.ArgumentParser(
99
+ prog="termux-aichain",
100
+ description="Sovereign Zero-Dependency AI Framework for Termux & Android Edge"
101
+ )
102
+ parser.add_argument("-v", "--version", action="version", version=f"%(prog)s {__version__}")
103
+ subparsers = parser.add_subparsers(dest="command", help="Available subcommands")
104
+
105
+ # setup
106
+ subparsers.add_parser("setup", help="Diagnose environment and check native tools")
107
+
108
+ # pull
109
+ pull_parser = subparsers.add_parser("pull", help="Download verified lightweight GGUF model")
110
+ pull_parser.add_argument("model", choices=list(MODELS_REGISTRY.keys()), help="Target model identifier")
111
+
112
+ # serve
113
+ serve_parser = subparsers.add_parser("serve", help="Launch 1-line REST/SSE/Web Dashboard server")
114
+ serve_parser.add_argument("--port", type=int, default=8080, help="Port to bind (default: 8080)")
115
+ serve_parser.add_argument("--host", type=str, default="0.0.0.0", help="Host to bind (default: 0.0.0.0)")
116
+
117
+ args = parser.parse_args()
118
+ if args.command == "setup":
119
+ cmd_setup()
120
+ elif args.command == "pull":
121
+ cmd_pull(args.model)
122
+ elif args.command == "serve":
123
+ cmd_serve(args.port, args.host)
124
+ else:
125
+ parser.print_help()
126
+
127
+ if __name__ == "__main__":
128
+ main()
@@ -0,0 +1,79 @@
1
+ """
2
+ ==============================================================================
3
+ termux-aichain Core Module Exports
4
+ ==============================================================================
5
+ """
6
+
7
+ from termux_aichain.core.schema import (
8
+ Message,
9
+ HumanMessage,
10
+ AIMessage,
11
+ SystemMessage,
12
+ ToolMessage,
13
+ UsageInfo,
14
+ GenerationResult,
15
+ StreamChunk,
16
+ )
17
+ from termux_aichain.core.prompt import (
18
+ PromptTemplate,
19
+ ChatPromptTemplate,
20
+ )
21
+ from termux_aichain.core.base import (
22
+ Runnable,
23
+ RunnableLambda,
24
+ RunnableSequence,
25
+ BaseChatModel,
26
+ )
27
+ from termux_aichain.core.providers.openai_compatible import OpenAICompatibleChat
28
+ from termux_aichain.core.providers.bitnet import BitNetChat
29
+ from termux_aichain.core.providers.local_server import (
30
+ LocalServerConfig,
31
+ LocalServerManager,
32
+ LlamaCppServer,
33
+ BitNetServer,
34
+ )
35
+ from termux_aichain.core.parsers import (
36
+ StringOutputParser,
37
+ JsonOutputParser,
38
+ RegexOutputParser,
39
+ )
40
+ from termux_aichain.core.splitters import (
41
+ Document,
42
+ CharacterTextSplitter,
43
+ RecursiveCharacterTextSplitter,
44
+ TextLoader,
45
+ MarkdownLoader,
46
+ JSONLoader,
47
+ )
48
+
49
+ __all__ = [
50
+ "Message",
51
+ "HumanMessage",
52
+ "AIMessage",
53
+ "SystemMessage",
54
+ "ToolMessage",
55
+ "UsageInfo",
56
+ "GenerationResult",
57
+ "StreamChunk",
58
+ "PromptTemplate",
59
+ "ChatPromptTemplate",
60
+ "Runnable",
61
+ "RunnableLambda",
62
+ "RunnableSequence",
63
+ "BaseChatModel",
64
+ "OpenAICompatibleChat",
65
+ "BitNetChat",
66
+ "LocalServerConfig",
67
+ "LocalServerManager",
68
+ "LlamaCppServer",
69
+ "BitNetServer",
70
+ "StringOutputParser",
71
+ "JsonOutputParser",
72
+ "RegexOutputParser",
73
+ "Document",
74
+ "CharacterTextSplitter",
75
+ "RecursiveCharacterTextSplitter",
76
+ "TextLoader",
77
+ "MarkdownLoader",
78
+ "JSONLoader",
79
+ ]
@@ -0,0 +1,144 @@
1
+ """
2
+ ==============================================================================
3
+ termux-aichain Core Engine: Runnable Base & Pipe Composition (|)
4
+ ==============================================================================
5
+ Provides standard Runnable, RunnableLambda, RunnableSequence interfaces.
6
+ Zero external heavy dependencies - Pure Python 3.10+ standard library.
7
+ """
8
+
9
+ from __future__ import annotations
10
+ import inspect
11
+ import asyncio
12
+ from abc import ABC, abstractmethod
13
+ from typing import Any, AsyncIterator, Callable, Dict, Iterator, List, Optional, Union
14
+ from termux_aichain.core.schema import Message, HumanMessage, AIMessage, GenerationResult, StreamChunk
15
+
16
+ class Runnable(ABC):
17
+ """Abstract Base Class for all executable chains, templates, and models."""
18
+
19
+ @abstractmethod
20
+ def invoke(self, input_data: Any, **kwargs: Any) -> Any:
21
+ pass
22
+
23
+ async def ainvoke(self, input_data: Any, **kwargs: Any) -> Any:
24
+ return await asyncio.to_thread(self.invoke, input_data, **kwargs)
25
+
26
+ def stream(self, input_data: Any, **kwargs: Any) -> Iterator[Any]:
27
+ yield self.invoke(input_data, **kwargs)
28
+
29
+ async def astream(self, input_data: Any, **kwargs: Any) -> AsyncIterator[Any]:
30
+ yield await self.ainvoke(input_data, **kwargs)
31
+
32
+ def __or__(self, other: Union[Runnable, Callable[[Any], Any]]) -> RunnableSequence:
33
+ right = other if isinstance(other, Runnable) else RunnableLambda(other)
34
+ return RunnableSequence(self, right)
35
+
36
+ def __ror__(self, other: Union[Runnable, Callable[[Any], Any]]) -> RunnableSequence:
37
+ left = other if isinstance(other, Runnable) else RunnableLambda(other)
38
+ return RunnableSequence(left, self)
39
+
40
+ class RunnableLambda(Runnable):
41
+ """Wraps any standard Python callable into a pipe-compatible Runnable."""
42
+
43
+ def __init__(self, func: Callable[[Any], Any]):
44
+ self.func = func
45
+
46
+ def invoke(self, input_data: Any, **kwargs: Any) -> Any:
47
+ if kwargs:
48
+ return self.func(input_data, **kwargs)
49
+ return self.func(input_data)
50
+
51
+ async def ainvoke(self, input_data: Any, **kwargs: Any) -> Any:
52
+ if inspect.iscoroutinefunction(self.func):
53
+ return await self.func(input_data, **kwargs)
54
+ return await asyncio.to_thread(self.invoke, input_data, **kwargs)
55
+
56
+ class RunnableSequence(Runnable):
57
+ """Executes multiple Runnables sequentially in a linear pipe chain."""
58
+
59
+ def __init__(self, *steps: Runnable):
60
+ self.steps: List[Runnable] = []
61
+ for step in steps:
62
+ if isinstance(step, RunnableSequence):
63
+ self.steps.extend(step.steps)
64
+ elif isinstance(step, Runnable):
65
+ self.steps.append(step)
66
+ elif callable(step):
67
+ self.steps.append(RunnableLambda(step))
68
+ else:
69
+ raise TypeError(f"Invalid step in sequence: {type(step)}")
70
+
71
+ def invoke(self, input_data: Any, **kwargs: Any) -> Any:
72
+ current = input_data
73
+ for step in self.steps:
74
+ current = step.invoke(current, **kwargs)
75
+ return current
76
+
77
+ async def ainvoke(self, input_data: Any, **kwargs: Any) -> Any:
78
+ current = input_data
79
+ for step in self.steps:
80
+ current = await step.ainvoke(current, **kwargs)
81
+ return current
82
+
83
+ def stream(self, input_data: Any, **kwargs: Any) -> Iterator[Any]:
84
+ if not self.steps:
85
+ return
86
+ if len(self.steps) == 1:
87
+ yield from self.steps[0].stream(input_data, **kwargs)
88
+ return
89
+
90
+ current = input_data
91
+ for step in self.steps[:-1]:
92
+ current = step.invoke(current, **kwargs)
93
+
94
+ yield from self.steps[-1].stream(current, **kwargs)
95
+
96
+ async def astream(self, input_data: Any, **kwargs: Any) -> AsyncIterator[Any]:
97
+ if not self.steps:
98
+ return
99
+ if len(self.steps) == 1:
100
+ async for chunk in self.steps[0].astream(input_data, **kwargs):
101
+ yield chunk
102
+ return
103
+
104
+ current = input_data
105
+ for step in self.steps[:-1]:
106
+ current = await step.ainvoke(current, **kwargs)
107
+
108
+ async for chunk in self.steps[-1].astream(current, **kwargs):
109
+ yield chunk
110
+
111
+ def __or__(self, other: Union[Runnable, Callable[[Any], Any]]) -> RunnableSequence:
112
+ right = other if isinstance(other, Runnable) else RunnableLambda(other)
113
+ return RunnableSequence(*self.steps, right)
114
+
115
+ class BaseChatModel(Runnable, ABC):
116
+ """Abstract Base Class for Chat Models."""
117
+
118
+ @abstractmethod
119
+ def generate(self, messages: List[Message]) -> GenerationResult:
120
+ pass
121
+
122
+ async def agenerate(self, messages: List[Message]) -> GenerationResult:
123
+ return await asyncio.to_thread(self.generate, messages)
124
+
125
+ def invoke(self, input_data: Union[str, List[Message], Dict[str, Any]], **kwargs: Any) -> GenerationResult:
126
+ messages = self._coerce_messages(input_data)
127
+ return self.generate(messages)
128
+
129
+ async def ainvoke(self, input_data: Union[str, List[Message], Dict[str, Any]], **kwargs: Any) -> GenerationResult:
130
+ messages = self._coerce_messages(input_data)
131
+ return await self.agenerate(messages)
132
+
133
+ def _coerce_messages(self, input_data: Union[str, List[Message], Dict[str, Any]]) -> List[Message]:
134
+ if isinstance(input_data, str):
135
+ return [HumanMessage(content=input_data)]
136
+ elif isinstance(input_data, list):
137
+ return input_data
138
+ elif isinstance(input_data, dict):
139
+ if "messages" in input_data:
140
+ return input_data["messages"]
141
+ elif "input" in input_data:
142
+ return [HumanMessage(content=str(input_data["input"]))]
143
+ return [HumanMessage(content=str(input_data))]
144
+ return [HumanMessage(content=str(input_data))]