grip-sdk 0.3.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 (40) hide show
  1. grip_sdk-0.3.0/LICENSE +21 -0
  2. grip_sdk-0.3.0/PKG-INFO +31 -0
  3. grip_sdk-0.3.0/README.md +19 -0
  4. grip_sdk-0.3.0/pyproject.toml +23 -0
  5. grip_sdk-0.3.0/setup.cfg +4 -0
  6. grip_sdk-0.3.0/src/grip/__init__.py +49 -0
  7. grip_sdk-0.3.0/src/grip/_version.py +3 -0
  8. grip_sdk-0.3.0/src/grip/async_client.py +161 -0
  9. grip_sdk-0.3.0/src/grip/cli/__init__.py +5 -0
  10. grip_sdk-0.3.0/src/grip/cli/main.py +160 -0
  11. grip_sdk-0.3.0/src/grip/client.py +415 -0
  12. grip_sdk-0.3.0/src/grip/config.py +35 -0
  13. grip_sdk-0.3.0/src/grip/exceptions.py +24 -0
  14. grip_sdk-0.3.0/src/grip/resources/__init__.py +13 -0
  15. grip_sdk-0.3.0/src/grip/resources/search.py +129 -0
  16. grip_sdk-0.3.0/src/grip/resources/sessions.py +140 -0
  17. grip_sdk-0.3.0/src/grip/types/__init__.py +17 -0
  18. grip_sdk-0.3.0/src/grip/types/response.py +7 -0
  19. grip_sdk-0.3.0/src/grip/types/session.py +7 -0
  20. grip_sdk-0.3.0/src/grip/types/source.py +5 -0
  21. grip_sdk-0.3.0/src/grip_core/__init__.py +52 -0
  22. grip_sdk-0.3.0/src/grip_core/config.py +86 -0
  23. grip_sdk-0.3.0/src/grip_core/errors.py +35 -0
  24. grip_sdk-0.3.0/src/grip_core/models/__init__.py +17 -0
  25. grip_sdk-0.3.0/src/grip_core/models/request.py +28 -0
  26. grip_sdk-0.3.0/src/grip_core/models/response.py +137 -0
  27. grip_sdk-0.3.0/src/grip_core/models/session.py +93 -0
  28. grip_sdk-0.3.0/src/grip_core/models/source.py +47 -0
  29. grip_sdk-0.3.0/src/grip_core/protocols.py +59 -0
  30. grip_sdk-0.3.0/src/grip_core/security/__init__.py +5 -0
  31. grip_sdk-0.3.0/src/grip_core/security/validation.py +32 -0
  32. grip_sdk-0.3.0/src/grip_sdk.egg-info/PKG-INFO +31 -0
  33. grip_sdk-0.3.0/src/grip_sdk.egg-info/SOURCES.txt +38 -0
  34. grip_sdk-0.3.0/src/grip_sdk.egg-info/dependency_links.txt +1 -0
  35. grip_sdk-0.3.0/src/grip_sdk.egg-info/entry_points.txt +2 -0
  36. grip_sdk-0.3.0/src/grip_sdk.egg-info/requires.txt +1 -0
  37. grip_sdk-0.3.0/src/grip_sdk.egg-info/top_level.txt +2 -0
  38. grip_sdk-0.3.0/tests/test_async_client.py +54 -0
  39. grip_sdk-0.3.0/tests/test_client.py +82 -0
  40. grip_sdk-0.3.0/tests/test_resources.py +46 -0
grip_sdk-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 theshoaib
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,31 @@
1
+ Metadata-Version: 2.4
2
+ Name: grip-sdk
3
+ Version: 0.3.0
4
+ Summary: Python client SDK for Grip Platform (https://grip.theshoaib.me/)
5
+ Author-email: theshoaib <shoaib@theshoaib.me>
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: requests>=2.33.0
11
+ Dynamic: license-file
12
+
13
+ # grip-sdk
14
+
15
+ Official Python SDK for Grip Platform (https://grip.theshoaib.me/).
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install grip-sdk
21
+ ```
22
+
23
+ ## Quickstart
24
+
25
+ ```python
26
+ import grip
27
+
28
+ client = grip.Client()
29
+ response = client.query("Quantum computing breakthroughs")
30
+ print(response.answer)
31
+ ```
@@ -0,0 +1,19 @@
1
+ # grip-sdk
2
+
3
+ Official Python SDK for Grip Platform (https://grip.theshoaib.me/).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install grip-sdk
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ ```python
14
+ import grip
15
+
16
+ client = grip.Client()
17
+ response = client.query("Quantum computing breakthroughs")
18
+ print(response.answer)
19
+ ```
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "grip-sdk"
7
+ version = "0.3.0"
8
+ description = "Python client SDK for Grip Platform (https://grip.theshoaib.me/)"
9
+ requires-python = ">=3.10"
10
+ readme = "README.md"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "theshoaib", email = "shoaib@theshoaib.me" }]
13
+
14
+ dependencies = [
15
+ "requests>=2.33.0",
16
+ ]
17
+
18
+
19
+ [project.scripts]
20
+ grip = "grip.cli.main:main"
21
+
22
+ [tool.setuptools.packages.find]
23
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,49 @@
1
+ """Grip: Enterprise AI Search, Dynamic Provider Orchestration, and Multi-Agent SDK.
2
+
3
+ https://grip.theshoaib.me/
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from ._version import __version__
9
+ from .async_client import AsyncClient, AsyncGrip, AsyncGripClient
10
+ from .client import Client, Grip, GripClient
11
+ from .config import ClientConfig, Config, get_config
12
+ from .exceptions import BotBlockedError, ConfigurationError, GripError, ProviderError
13
+ from .types import (
14
+ Metadata,
15
+ SearchResponse,
16
+ SessionDetail,
17
+ SessionMessage,
18
+ SessionSummary,
19
+ SourceItem,
20
+ StreamChunk,
21
+ )
22
+ from grip_engine.infrastructure.cache import TTLCache
23
+ from grip_engine.infrastructure.rate_limit import RateLimiter
24
+
25
+ __all__ = [
26
+ "__version__",
27
+ "Grip",
28
+ "Client",
29
+ "GripClient",
30
+ "AsyncGrip",
31
+ "AsyncClient",
32
+ "AsyncGripClient",
33
+ "Config",
34
+ "ClientConfig",
35
+ "get_config",
36
+ "RateLimiter",
37
+ "TTLCache",
38
+ "SearchResponse",
39
+ "StreamChunk",
40
+ "Metadata",
41
+ "SourceItem",
42
+ "SessionDetail",
43
+ "SessionSummary",
44
+ "SessionMessage",
45
+ "GripError",
46
+ "ProviderError",
47
+ "BotBlockedError",
48
+ "ConfigurationError",
49
+ ]
@@ -0,0 +1,3 @@
1
+ """Package version info."""
2
+
3
+ __version__ = "0.3.0"
@@ -0,0 +1,161 @@
1
+ """Grip Python SDK Asynchronous Client.
2
+
3
+ Enterprise asynchronous AI Search, Provider Agnostic Orchestration,
4
+ and Multi-turn Conversational Sessions.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ from collections.abc import AsyncGenerator
11
+ from typing import Any
12
+
13
+ from grip_core.config import GripConfig, load_config
14
+ from grip_core.models.response import Metadata, SearchResponse, StreamChunk
15
+ from grip_core.models.session import SessionDetail, SessionSummary
16
+ from grip_core.models.source import SourceItem
17
+
18
+ from .client import Grip
19
+ from .config import ClientConfig
20
+ from .resources.search import AsyncSearchResource
21
+ from .resources.sessions import AsyncSessionsResource
22
+
23
+
24
+ class AsyncGrip:
25
+ """Primary asynchronous client for Grip Platform.
26
+
27
+ Usage:
28
+ >>> import asyncio
29
+ >>> from grip import AsyncGrip
30
+ >>>
31
+ >>> async def main():
32
+ ... client = AsyncGrip()
33
+ ... response = await client.search.query("Quantum computing breakthroughs")
34
+ ... print(response.answer)
35
+ ... await client.close()
36
+ >>> asyncio.run(main())
37
+ """
38
+
39
+ def __init__(
40
+ self,
41
+ base_url: str | None = None,
42
+ api_key: str | None = None,
43
+ config: GripConfig | ClientConfig | None = None,
44
+ engine: Any = None,
45
+ provider: str = "google",
46
+ timeout: float = 60.0,
47
+ ) -> None:
48
+ self._sync_client = Grip(
49
+ base_url=base_url,
50
+ api_key=api_key,
51
+ config=config,
52
+ engine=engine,
53
+ provider=provider,
54
+ timeout=timeout,
55
+ )
56
+ self.provider = provider
57
+ self.search = AsyncSearchResource(client=self)
58
+ self.sessions = AsyncSessionsResource(client=self)
59
+
60
+ async def _execute_query(
61
+ self,
62
+ query: str,
63
+ session_id: str | None = None,
64
+ provider: str | None = None,
65
+ thread_id: str | None = None,
66
+ timeout: float | None = None,
67
+ ) -> SearchResponse:
68
+ return await asyncio.to_thread(
69
+ self._sync_client._execute_query,
70
+ query=query,
71
+ session_id=session_id,
72
+ provider=provider or self.provider,
73
+ thread_id=thread_id,
74
+ timeout=timeout,
75
+ )
76
+
77
+ async def _execute_stream(
78
+ self,
79
+ query: str,
80
+ session_id: str | None = None,
81
+ provider: str | None = None,
82
+ thread_id: str | None = None,
83
+ ) -> AsyncGenerator[StreamChunk, None]:
84
+ def _get_chunks() -> list[StreamChunk]:
85
+ return list(
86
+ self._sync_client._execute_stream(
87
+ query=query,
88
+ session_id=session_id,
89
+ provider=provider or self.provider,
90
+ thread_id=thread_id,
91
+ )
92
+ )
93
+
94
+ chunks = await asyncio.to_thread(_get_chunks)
95
+ for chunk in chunks:
96
+ yield chunk
97
+
98
+ async def _create_session(
99
+ self,
100
+ title: str | None = None,
101
+ session_id: str | None = None,
102
+ metadata: dict[str, Any] | None = None,
103
+ ) -> SessionDetail:
104
+ return await asyncio.to_thread(
105
+ self._sync_client._create_session,
106
+ title=title,
107
+ session_id=session_id,
108
+ metadata=metadata,
109
+ )
110
+
111
+ async def _get_session(self, session_id: str) -> SessionDetail | None:
112
+ return await asyncio.to_thread(self._sync_client._get_session, session_id)
113
+
114
+ async def _list_sessions(self) -> list[SessionSummary]:
115
+ return await asyncio.to_thread(self._sync_client._list_sessions)
116
+
117
+ async def _delete_session(self, session_id: str) -> bool:
118
+ return await asyncio.to_thread(self._sync_client._delete_session, session_id)
119
+
120
+ async def _clear_sessions(self) -> int:
121
+ return await asyncio.to_thread(self._sync_client._clear_sessions)
122
+
123
+ async def query(
124
+ self,
125
+ query: str,
126
+ session_id: str | None = None,
127
+ provider: str | None = None,
128
+ timeout: float | None = None,
129
+ ) -> SearchResponse:
130
+ return await self.search.query(
131
+ query=query,
132
+ session_id=session_id,
133
+ provider=provider,
134
+ timeout=timeout,
135
+ )
136
+
137
+ async def stream(
138
+ self,
139
+ query: str,
140
+ session_id: str | None = None,
141
+ provider: str | None = None,
142
+ ) -> AsyncGenerator[StreamChunk, None]:
143
+ async for chunk in self.search.stream(
144
+ query=query,
145
+ session_id=session_id,
146
+ provider=provider,
147
+ ):
148
+ yield chunk
149
+
150
+ async def close(self) -> None:
151
+ await asyncio.to_thread(self._sync_client.close)
152
+
153
+ async def __aenter__(self) -> AsyncGrip:
154
+ return self
155
+
156
+ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
157
+ await self.close()
158
+
159
+
160
+ AsyncClient = AsyncGrip
161
+ AsyncGripClient = AsyncGrip
@@ -0,0 +1,5 @@
1
+ """Grip CLI package."""
2
+
3
+ from .main import main
4
+
5
+ __all__ = ["main"]
@@ -0,0 +1,160 @@
1
+ """Grip CLI: Command-line interface for Grip AI Search & Web Agent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from typing import Sequence
9
+
10
+ from ..client import Grip
11
+ from .._version import __version__
12
+
13
+
14
+ def main(argv: Sequence[str] | None = None) -> int:
15
+ parser = argparse.ArgumentParser(
16
+ prog="grip",
17
+ description="Grip Platform: Dynamic AI Search & Multi-Agent SDK (https://grip.theshoaib.me/)",
18
+ )
19
+ parser.add_argument("--version", "-v", action="version", version=f"Grip Platform v{__version__}")
20
+ subparsers = parser.add_subparsers(dest="command", help="Sub-commands")
21
+
22
+ # Search sub-command
23
+ search_parser = subparsers.add_parser("search", aliases=["query"], help="Execute an AI search query")
24
+ search_parser.add_argument("query", help="Search query string")
25
+ search_parser.add_argument("--json", action="store_true", help="Output raw JSON response")
26
+ search_parser.add_argument("--session-id", help="Attach query to existing session ID")
27
+ search_parser.add_argument("--provider", default="google", help="Search AI provider (google, openai, etc.)")
28
+ search_parser.add_argument("--no-stream", action="store_true", help="Disable streaming and wait for full response")
29
+
30
+ # Interactive chat sub-command
31
+ chat_parser = subparsers.add_parser("chat", aliases=["session"], help="Start an interactive multi-turn session in terminal")
32
+ chat_parser.add_argument("--title", default="Grip Chat", help="Session title")
33
+ chat_parser.add_argument("--provider", default="google", help="Search AI provider (google, openai, etc.)")
34
+
35
+ # Providers sub-command
36
+ subparsers.add_parser("providers", help="List available AI search providers")
37
+
38
+ # Serve sub-command
39
+ serve_parser = subparsers.add_parser("serve", help="Start the Grip AI web server and UI")
40
+ serve_parser.add_argument("--host", default="0.0.0.0", help="Host interface (default: 0.0.0.0)")
41
+ serve_parser.add_argument("--port", type=int, default=8000, help="Port (default: 8000)")
42
+ serve_parser.add_argument("--debug", action="store_true", help="Enable debug mode")
43
+
44
+ # Rules sub-command
45
+ rules_parser = subparsers.add_parser("rules", help="Manage custom system rules and persona")
46
+ rules_parser.add_argument("--identity", help="Set assistant identity/persona")
47
+ rules_parser.add_argument("--format", help="Set response format specification")
48
+ rules_parser.add_argument("--add", help="Add a behavioral constraint rule")
49
+ rules_parser.add_argument("--list", action="store_true", help="List active rules")
50
+
51
+ args = parser.parse_args(argv)
52
+
53
+ if not args.command:
54
+ parser.print_help()
55
+ return 0
56
+
57
+ if args.command == "providers":
58
+ from grip_engine.providers.registry import default_provider_registry
59
+ providers = default_provider_registry.list_providers()
60
+ print("\nRegistered AI Search Providers:")
61
+ for p in providers:
62
+ print(f"• {p}")
63
+ return 0
64
+
65
+ if args.command == "serve":
66
+ try:
67
+ import uvicorn
68
+ from services.api.app.main import app
69
+ print(f"🚀 Starting Grip AI Platform on http://{args.host}:{args.port}")
70
+ uvicorn.run(app, host=args.host, port=args.port)
71
+ return 0
72
+ except Exception:
73
+ # Fallback to app.server
74
+ from app.server import create_app
75
+ from google_ai_search.config import get_config
76
+ cfg = get_config()
77
+ cfg.port = args.port
78
+ cfg.debug = args.debug
79
+ flask_app = create_app(cfg)
80
+ print(f"🚀 Starting Grip AI Search server on http://{args.host}:{args.port}")
81
+ flask_app.run(host=args.host, port=args.port, debug=args.debug)
82
+ return 0
83
+
84
+ if args.command in ("search", "query"):
85
+ client = Grip(provider=args.provider)
86
+ if args.json:
87
+ res = client.query(args.query, session_id=args.session_id, provider=args.provider)
88
+ print(json.dumps(res.to_dict(), indent=2))
89
+ return 0
90
+
91
+ if args.no_stream:
92
+ res = client.query(args.query, session_id=args.session_id, provider=args.provider)
93
+ print(res.answer)
94
+ return 0
95
+
96
+ try:
97
+ thought_printed = False
98
+ for chunk in client.stream(args.query, session_id=args.session_id, provider=args.provider):
99
+ if chunk.event == "thinking" and chunk.thinking and not thought_printed:
100
+ thought_printed = True
101
+ print(f"💭 [{chunk.thinking}]", flush=True)
102
+ elif chunk.event == "delta" and chunk.delta:
103
+ sys.stdout.write(chunk.delta)
104
+ sys.stdout.flush()
105
+ sys.stdout.write("\n")
106
+ return 0
107
+ except Exception as exc:
108
+ print(f"Error: {exc}", file=sys.stderr)
109
+ return 1
110
+
111
+ if args.command in ("chat", "session"):
112
+ client = Grip(provider=args.provider)
113
+ session = client.create_session(title=args.title)
114
+ print(f"✨ Started Grip interactive session ({session.session_id}) [Provider: {args.provider}]")
115
+ print("Type your questions below. Type 'exit' or 'quit' to end.\n")
116
+
117
+ while True:
118
+ try:
119
+ query = input("\n[You] > ").strip()
120
+ if not query:
121
+ continue
122
+ if query.lower() in ("exit", "quit"):
123
+ print("Goodbye!")
124
+ break
125
+ print("\n[Grip AI] > ", end="", flush=True)
126
+ for chunk in client.stream(query, session_id=session.session_id, provider=args.provider):
127
+ if chunk.event == "delta" and chunk.delta:
128
+ sys.stdout.write(chunk.delta)
129
+ sys.stdout.flush()
130
+ sys.stdout.write("\n")
131
+ except (KeyboardInterrupt, EOFError):
132
+ print("\nSession ended.")
133
+ break
134
+ except Exception as exc:
135
+ print(f"Error: {exc}", file=sys.stderr)
136
+ return 0
137
+
138
+ if args.command == "rules":
139
+ client = Grip()
140
+ if args.identity:
141
+ client.rules.set_identity(args.identity)
142
+ print(f"✅ Set identity: {args.identity}")
143
+ if args.format:
144
+ client.rules.set_response_format(args.format)
145
+ print(f"✅ Set response format: {args.format}")
146
+ if args.add:
147
+ r = client.rules.add(args.add)
148
+ print(f"✅ Added rule: {r.name}")
149
+ if args.list or (not args.identity and not args.format and not args.add):
150
+ active_rules = client.rules.list(enabled_only=True)
151
+ print("\nActive System Rules:")
152
+ for rule in active_rules:
153
+ print(f"• [P{rule.priority}] {rule.name}: {rule.content}")
154
+ return 0
155
+
156
+ return 0
157
+
158
+
159
+ if __name__ == "__main__":
160
+ sys.exit(main())