kalibr 1.0.17__py3-none-any.whl → 1.0.20__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.
@@ -0,0 +1,347 @@
1
+ """
2
+ Enhanced Kalibr App Example - App-level capabilities
3
+ This demonstrates the new enhanced capabilities including file uploads,
4
+ sessions, streaming, workflows, and multi-model schema generation.
5
+ """
6
+
7
+ from kalibr import KalibrApp
8
+ from kalibr.types import FileUpload, Session, StreamingResponse, WorkflowState, AuthenticatedUser
9
+ import asyncio
10
+ import json
11
+ from datetime import datetime
12
+ from typing import List
13
+
14
+ # Create an enhanced KalibrApp instance
15
+ app = KalibrApp(title="Enhanced Kalibr Demo", base_url="http://localhost:8000")
16
+
17
+ # Basic action (compatible with original Kalibr)
18
+ @app.action("hello", "Say hello with enhanced capabilities")
19
+ def hello_enhanced(name: str = "World", include_timestamp: bool = False):
20
+ """Enhanced hello function with optional timestamp"""
21
+ message = f"Hello, {name}! This is Enhanced Kalibr v2.0"
22
+
23
+ response = {"message": message}
24
+ if include_timestamp:
25
+ response["timestamp"] = datetime.now().isoformat()
26
+
27
+ return response
28
+
29
+ # File upload handler
30
+ @app.file_handler("analyze_document", [".txt", ".md", ".py", ".js", ".json"])
31
+ async def analyze_document(file: FileUpload):
32
+ """Analyze uploaded document and return insights"""
33
+ try:
34
+ # Decode file content
35
+ content = file.content.decode('utf-8')
36
+
37
+ # Basic analysis
38
+ lines = content.split('\n')
39
+ words = content.split()
40
+
41
+ # Language detection based on file extension
42
+ language = "text"
43
+ if file.filename.endswith('.py'):
44
+ language = "python"
45
+ elif file.filename.endswith('.js'):
46
+ language = "javascript"
47
+ elif file.filename.endswith('.json'):
48
+ language = "json"
49
+ try:
50
+ json_data = json.loads(content)
51
+ return {
52
+ "upload_id": file.upload_id,
53
+ "filename": file.filename,
54
+ "analysis": {
55
+ "type": "json",
56
+ "valid_json": True,
57
+ "keys": list(json_data.keys()) if isinstance(json_data, dict) else None,
58
+ "size_bytes": file.size
59
+ }
60
+ }
61
+ except json.JSONDecodeError:
62
+ pass
63
+
64
+ return {
65
+ "upload_id": file.upload_id,
66
+ "filename": file.filename,
67
+ "analysis": {
68
+ "language": language,
69
+ "line_count": len(lines),
70
+ "word_count": len(words),
71
+ "character_count": len(content),
72
+ "size_bytes": file.size,
73
+ "non_empty_lines": len([line for line in lines if line.strip()]),
74
+ "estimated_reading_time_minutes": len(words) / 200 # Average reading speed
75
+ }
76
+ }
77
+ except UnicodeDecodeError:
78
+ return {
79
+ "upload_id": file.upload_id,
80
+ "filename": file.filename,
81
+ "error": "File is not text-readable (binary file)",
82
+ "size_bytes": file.size
83
+ }
84
+
85
+ # Session-aware action
86
+ @app.session_action("save_note", "Save a note to user session")
87
+ async def save_note(session: Session, note_title: str, note_content: str):
88
+ """Save a note to the user's session"""
89
+
90
+ # Initialize notes if not exists
91
+ if 'notes' not in session.data:
92
+ session.data['notes'] = []
93
+
94
+ # Create note object
95
+ note = {
96
+ "id": len(session.data['notes']) + 1,
97
+ "title": note_title,
98
+ "content": note_content,
99
+ "created_at": datetime.now().isoformat(),
100
+ "updated_at": datetime.now().isoformat()
101
+ }
102
+
103
+ session.data['notes'].append(note)
104
+ session.set('last_note_id', note['id'])
105
+
106
+ return {
107
+ "status": "saved",
108
+ "note": note,
109
+ "total_notes": len(session.data['notes']),
110
+ "session_id": session.session_id
111
+ }
112
+
113
+ @app.session_action("get_notes", "Retrieve all notes from session")
114
+ async def get_notes(session: Session):
115
+ """Get all notes from the user's session"""
116
+ notes = session.get('notes', [])
117
+
118
+ return {
119
+ "notes": notes,
120
+ "count": len(notes),
121
+ "session_id": session.session_id,
122
+ "last_note_id": session.get('last_note_id')
123
+ }
124
+
125
+ # Streaming action
126
+ @app.stream_action("count_with_progress", "Stream counting with progress updates")
127
+ async def count_with_progress(max_count: int = 10, delay_seconds: float = 1.0):
128
+ """Stream counting numbers with progress indication"""
129
+
130
+ for i in range(max_count + 1):
131
+ progress_percent = (i / max_count) * 100
132
+
133
+ yield {
134
+ "count": i,
135
+ "max_count": max_count,
136
+ "progress_percent": progress_percent,
137
+ "message": f"Counting: {i}/{max_count}",
138
+ "timestamp": datetime.now().isoformat(),
139
+ "is_complete": (i == max_count)
140
+ }
141
+
142
+ if i < max_count: # Don't delay after the last item
143
+ await asyncio.sleep(delay_seconds)
144
+
145
+ @app.stream_action("generate_fibonacci", "Stream Fibonacci sequence")
146
+ async def generate_fibonacci(count: int = 20, delay_seconds: float = 0.5):
147
+ """Generate Fibonacci sequence as a stream"""
148
+
149
+ a, b = 0, 1
150
+ for i in range(count):
151
+ yield {
152
+ "position": i + 1,
153
+ "fibonacci_number": a,
154
+ "sequence_so_far": f"F({i+1}) = {a}",
155
+ "timestamp": datetime.now().isoformat()
156
+ }
157
+
158
+ a, b = b, a + b
159
+ await asyncio.sleep(delay_seconds)
160
+
161
+ # Complex workflow
162
+ @app.workflow("process_text_analysis", "Complete text analysis workflow")
163
+ async def text_analysis_workflow(text: str, workflow_state: WorkflowState):
164
+ """Multi-step text analysis workflow"""
165
+
166
+ # Step 1: Validation
167
+ workflow_state.step = "validation"
168
+ workflow_state.status = "processing"
169
+
170
+ if not text or len(text.strip()) < 10:
171
+ workflow_state.status = "error"
172
+ return {"error": "Text must be at least 10 characters long"}
173
+
174
+ await asyncio.sleep(1) # Simulate processing time
175
+
176
+ # Step 2: Basic analysis
177
+ workflow_state.step = "basic_analysis"
178
+ workflow_state.data["validation_passed"] = True
179
+
180
+ words = text.split()
181
+ sentences = [s.strip() for s in text.replace('!', '.').replace('?', '.').split('.') if s.strip()]
182
+
183
+ basic_stats = {
184
+ "character_count": len(text),
185
+ "word_count": len(words),
186
+ "sentence_count": len(sentences),
187
+ "paragraph_count": len([p for p in text.split('\n\n') if p.strip()])
188
+ }
189
+
190
+ workflow_state.data["basic_stats"] = basic_stats
191
+ await asyncio.sleep(1)
192
+
193
+ # Step 3: Advanced analysis
194
+ workflow_state.step = "advanced_analysis"
195
+
196
+ # Word frequency
197
+ word_freq = {}
198
+ for word in words:
199
+ clean_word = word.lower().strip('.,!?";:')
200
+ word_freq[clean_word] = word_freq.get(clean_word, 0) + 1
201
+
202
+ # Top words
203
+ top_words = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:5]
204
+
205
+ advanced_stats = {
206
+ "unique_words": len(word_freq),
207
+ "average_word_length": sum(len(word) for word in words) / len(words) if words else 0,
208
+ "longest_word": max(words, key=len) if words else None,
209
+ "top_words": top_words,
210
+ "readability_score": min(100, max(0, 100 - (len(words) / len(sentences) if sentences else 1) * 2))
211
+ }
212
+
213
+ workflow_state.data["advanced_stats"] = advanced_stats
214
+ await asyncio.sleep(1)
215
+
216
+ # Step 4: Final compilation
217
+ workflow_state.step = "compilation"
218
+
219
+ result = {
220
+ "workflow_id": workflow_state.workflow_id,
221
+ "analysis_type": "complete_text_analysis",
222
+ "input_text_preview": text[:100] + "..." if len(text) > 100 else text,
223
+ "basic_statistics": basic_stats,
224
+ "advanced_statistics": advanced_stats,
225
+ "processing_steps": ["validation", "basic_analysis", "advanced_analysis", "compilation"],
226
+ "completed_at": datetime.now().isoformat()
227
+ }
228
+
229
+ workflow_state.step = "completed"
230
+ workflow_state.status = "success"
231
+ workflow_state.data["final_result"] = result
232
+
233
+ return result
234
+
235
+ # Data processing workflow
236
+ @app.workflow("batch_text_processor", "Process multiple texts in batch")
237
+ async def batch_text_processor(texts: List[str], workflow_state: WorkflowState):
238
+ """Process multiple texts as a batch workflow"""
239
+
240
+ workflow_state.step = "initialization"
241
+ workflow_state.status = "processing"
242
+
243
+ if not texts or len(texts) == 0:
244
+ workflow_state.status = "error"
245
+ return {"error": "No texts provided for processing"}
246
+
247
+ results = []
248
+ workflow_state.data["total_texts"] = len(texts)
249
+
250
+ for i, text in enumerate(texts):
251
+ workflow_state.step = f"processing_text_{i+1}"
252
+ workflow_state.data["current_text"] = i + 1
253
+ workflow_state.data["progress_percent"] = ((i + 1) / len(texts)) * 100
254
+
255
+ # Process each text
256
+ words = text.split()
257
+ analysis = {
258
+ "text_id": i + 1,
259
+ "text_preview": text[:50] + "..." if len(text) > 50 else text,
260
+ "word_count": len(words),
261
+ "character_count": len(text),
262
+ "sentence_count": len([s for s in text.split('.') if s.strip()])
263
+ }
264
+
265
+ results.append(analysis)
266
+ await asyncio.sleep(0.5) # Simulate processing time
267
+
268
+ # Final aggregation
269
+ workflow_state.step = "aggregation"
270
+
271
+ total_words = sum(r["word_count"] for r in results)
272
+ total_chars = sum(r["character_count"] for r in results)
273
+
274
+ final_result = {
275
+ "workflow_id": workflow_state.workflow_id,
276
+ "batch_summary": {
277
+ "total_texts_processed": len(results),
278
+ "total_words": total_words,
279
+ "total_characters": total_chars,
280
+ "average_words_per_text": total_words / len(results) if results else 0,
281
+ "average_chars_per_text": total_chars / len(results) if results else 0
282
+ },
283
+ "individual_results": results,
284
+ "completed_at": datetime.now().isoformat()
285
+ }
286
+
287
+ workflow_state.step = "completed"
288
+ workflow_state.status = "success"
289
+ workflow_state.data["final_result"] = final_result
290
+
291
+ return final_result
292
+
293
+ # Advanced action with multiple parameters
294
+ @app.action("advanced_search", "Perform advanced search with multiple filters")
295
+ def advanced_search(
296
+ query: str,
297
+ category: str = "all",
298
+ min_score: float = 0.0,
299
+ max_results: int = 10,
300
+ include_metadata: bool = False,
301
+ sort_by: str = "relevance"
302
+ ):
303
+ """Advanced search function demonstrating complex parameter handling"""
304
+
305
+ # Simulate search results
306
+ mock_results = [
307
+ {"id": 1, "title": f"Result matching '{query}'", "score": 0.95, "category": category},
308
+ {"id": 2, "title": f"Another match for '{query}'", "score": 0.87, "category": category},
309
+ {"id": 3, "title": f"Related to '{query}'", "score": 0.73, "category": category},
310
+ ]
311
+
312
+ # Filter by score
313
+ filtered_results = [r for r in mock_results if r["score"] >= min_score]
314
+
315
+ # Limit results
316
+ filtered_results = filtered_results[:max_results]
317
+
318
+ # Sort results
319
+ if sort_by == "score":
320
+ filtered_results.sort(key=lambda x: x["score"], reverse=True)
321
+
322
+ response = {
323
+ "query": query,
324
+ "filters": {
325
+ "category": category,
326
+ "min_score": min_score,
327
+ "max_results": max_results,
328
+ "sort_by": sort_by
329
+ },
330
+ "results": filtered_results,
331
+ "result_count": len(filtered_results)
332
+ }
333
+
334
+ if include_metadata:
335
+ response["metadata"] = {
336
+ "search_performed_at": datetime.now().isoformat(),
337
+ "processing_time_ms": 45,
338
+ "total_available": len(mock_results)
339
+ }
340
+
341
+ return response
342
+
343
+ # Enable authentication (optional)
344
+ # app.enable_auth("your-secret-jwt-key-here")
345
+
346
+ # The app instance is automatically discovered by the Kalibr CLI
347
+ # To run this: kalibr serve enhanced_kalibr_example.py --app-mode
@@ -0,0 +1,302 @@
1
+ Metadata-Version: 2.4
2
+ Name: kalibr
3
+ Version: 1.0.20
4
+ Summary: Multi-Model AI Integration Framework
5
+ Home-page: https://github.com/devonakelley/kalibr-sdk
6
+ Author: Kalibr Team
7
+ Author-email: Kalibr Team <team@kalibr.dev>
8
+ License: MIT
9
+ Project-URL: Homepage, https://kalibr.dev
10
+ Project-URL: Documentation, https://kalibr.dev/docs
11
+ Project-URL: Repository, https://github.com/devonakelley/kalibr-sdk
12
+ Project-URL: Bug Reports, https://github.com/devonakelley/kalibr-sdk/issues
13
+ Keywords: ai,api,framework,gpt,claude,gemini,copilot,multi-model,sdk
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
23
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
24
+ Requires-Python: >=3.11
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: fastapi>=0.110.1
28
+ Requires-Dist: uvicorn>=0.25.0
29
+ Requires-Dist: pydantic>=2.6.4
30
+ Requires-Dist: typer>=0.9.0
31
+ Requires-Dist: requests>=2.31.0
32
+ Requires-Dist: python-jose[cryptography]>=3.3.0
33
+ Requires-Dist: passlib[bcrypt]>=1.7.4
34
+ Requires-Dist: python-multipart>=0.0.9
35
+ Requires-Dist: motor>=3.3.1
36
+ Requires-Dist: pymongo>=4.5.0
37
+ Requires-Dist: boto3>=1.34.129
38
+ Requires-Dist: aiofiles>=23.2.1
39
+ Provides-Extra: dev
40
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
41
+ Requires-Dist: black>=24.1.1; extra == "dev"
42
+ Requires-Dist: isort>=5.13.2; extra == "dev"
43
+ Requires-Dist: flake8>=7.0.0; extra == "dev"
44
+ Requires-Dist: mypy>=1.8.0; extra == "dev"
45
+ Dynamic: author
46
+ Dynamic: home-page
47
+ Dynamic: license-file
48
+ Dynamic: requires-python
49
+
50
+ # Kalibr SDK
51
+
52
+ **Multi-Model AI Integration Framework**
53
+
54
+ Write once. Deploy anywhere. Connect to any AI model.
55
+
56
+ Kalibr lets you expose Python functions as APIs that work with **GPT, Claude, Gemini, and Copilot** automatically.
57
+
58
+ [![PyPI version](https://badge.fury.io/py/kalibr.svg)](https://badge.fury.io/py/kalibr)
59
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
60
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
61
+
62
+ ---
63
+
64
+ ## 🚀 Quick Start (2 minutes)
65
+
66
+ ### 1. Install
67
+ ```bash
68
+ pip install kalibr
69
+ ```
70
+
71
+ ### 2. Get Examples
72
+ ```bash
73
+ kalibr-connect examples
74
+ ```
75
+
76
+ This copies example files to `./kalibr_examples/` in your current directory.
77
+
78
+ ### 3. Run Demo
79
+ ```bash
80
+ kalibr-connect serve kalibr_examples/basic_kalibr_example.py
81
+ ```
82
+
83
+ ### 4. See Multi-Model Schemas
84
+
85
+ Open your browser to see all 4 AI model schemas auto-generated:
86
+
87
+ ```
88
+ http://localhost:8000/ # API info
89
+ http://localhost:8000/gpt-actions.json # For ChatGPT
90
+ http://localhost:8000/mcp.json # For Claude
91
+ http://localhost:8000/schemas/gemini # For Gemini
92
+ http://localhost:8000/schemas/copilot # For Copilot
93
+ http://localhost:8000/docs # Interactive docs
94
+ ```
95
+
96
+ **That's it!** One Python file, four AI platform schemas. 🎯
97
+
98
+ ---
99
+
100
+ ## 🎯 What Does This Do?
101
+
102
+ Kalibr turns your Python functions into APIs that AI assistants can call:
103
+
104
+ ```python
105
+ from kalibr import Kalibr
106
+
107
+ app = Kalibr(title="My Business API")
108
+
109
+ @app.action("get_inventory", "Check product stock")
110
+ def get_inventory(product_id: str):
111
+ # Your business logic
112
+ return {"product_id": product_id, "stock": 42}
113
+ ```
114
+
115
+ **Result:** ChatGPT, Claude, Gemini, and Copilot can all call your `get_inventory` function!
116
+
117
+ ---
118
+
119
+ ## 💪 Two Modes
120
+
121
+ ### Function-Level (Simple)
122
+ Perfect for exposing business logic:
123
+
124
+ ```python
125
+ from kalibr import Kalibr
126
+
127
+ app = Kalibr(title="My API")
128
+
129
+ @app.action("calculate_price", "Calculate product price")
130
+ def calculate_price(product_id: str, quantity: int):
131
+ return {"total": quantity * get_price(product_id)}
132
+ ```
133
+
134
+ ### App-Level (Advanced)
135
+ Full framework with file uploads, sessions, streaming, workflows:
136
+
137
+ ```python
138
+ from kalibr import KalibrApp
139
+ from kalibr.types import FileUpload, Session
140
+
141
+ app = KalibrApp(title="Advanced API")
142
+
143
+ @app.file_handler("analyze_doc", [".pdf", ".docx"])
144
+ async def analyze_doc(file: FileUpload):
145
+ return {"filename": file.filename, "analysis": "..."}
146
+
147
+ @app.session_action("save_data", "Save to session")
148
+ async def save_data(session: Session, data: dict):
149
+ session.set("my_data", data)
150
+ return {"saved": True}
151
+
152
+ @app.stream_action("live_feed", "Stream real-time data")
153
+ async def live_feed(count: int = 10):
154
+ for i in range(count):
155
+ yield {"item": i, "timestamp": "..."}
156
+ ```
157
+
158
+ ---
159
+
160
+ ## 📚 Examples Included
161
+
162
+ After running `kalibr-connect examples`, you get:
163
+
164
+ - **`basic_kalibr_example.py`** - Simple function-level example
165
+ - **`enhanced_kalibr_example.py`** - Advanced app-level example with all features
166
+
167
+ ---
168
+
169
+ ## 🤖 AI Platform Integration
170
+
171
+ Once your Kalibr app is running, integrate with AI platforms:
172
+
173
+ ### ChatGPT (GPT Actions)
174
+ 1. Copy schema from `http://localhost:8000/gpt-actions.json`
175
+ 2. Go to GPT Builder → Actions → Import from URL
176
+ 3. Done! ChatGPT can now call your functions
177
+
178
+ ### Claude (MCP)
179
+ 1. Add to Claude Desktop config:
180
+ ```json
181
+ {
182
+ "mcp": {
183
+ "servers": {
184
+ "my-api": {
185
+ "url": "http://localhost:8000/mcp.json"
186
+ }
187
+ }
188
+ }
189
+ }
190
+ ```
191
+ 2. Done! Claude can now call your functions
192
+
193
+ ### Gemini & Copilot
194
+ Similar simple setup using their respective schema endpoints.
195
+
196
+ ---
197
+
198
+ ## 🎯 Use Cases
199
+
200
+ - **Customer Service APIs** - Let AI assistants look up orders, process refunds
201
+ - **Data Analysis APIs** - Let AI query your analytics and generate insights
202
+ - **Document Processing** - Let AI analyze uploaded documents
203
+ - **Business Automation** - Let AI trigger workflows in your systems
204
+ - **Internal Tools** - Give your team AI-powered access to internal systems
205
+
206
+ ---
207
+
208
+ ## 📖 Documentation
209
+
210
+ - **Quick Start**: You're reading it!
211
+ - **Full Docs**: See `KALIBR_SDK_COMPLETE.md` in the package
212
+ - **Examples**: Run `kalibr-connect examples`
213
+ - **CLI Help**: `kalibr-connect --help`
214
+
215
+ ---
216
+
217
+ ## 🔧 CLI Commands
218
+
219
+ ```bash
220
+ kalibr-connect examples # Copy example files to current dir
221
+ kalibr-connect serve my_app.py # Run your app locally
222
+ kalibr-connect version # Show version info
223
+ kalibr-connect --help # Show all commands
224
+ ```
225
+
226
+ ---
227
+
228
+ ## ⚡ Key Features
229
+
230
+ - ✅ **Multi-Model Support** - Works with GPT, Claude, Gemini, Copilot
231
+ - ✅ **Automatic Schemas** - No manual schema writing
232
+ - ✅ **File Uploads** - Handle document uploads
233
+ - ✅ **Sessions** - Stateful conversations
234
+ - ✅ **Streaming** - Real-time data streaming
235
+ - ✅ **Workflows** - Multi-step processes
236
+ - ✅ **Type Safe** - Full Python type hints
237
+ - ✅ **Fast** - Async/await support
238
+
239
+ ---
240
+
241
+ ## 🔥 Why Kalibr?
242
+
243
+ **Without Kalibr:**
244
+ - Learn 4 different API specs
245
+ - Write 4 different schemas
246
+ - Maintain 4 codebases
247
+ - = Weeks of work
248
+
249
+ **With Kalibr:**
250
+ - Write Python functions once
251
+ - Kalibr generates all 4 schemas
252
+ - Single codebase
253
+ - = One day of work
254
+
255
+ ---
256
+
257
+ ## 💡 Simple Example
258
+
259
+ ```python
260
+ # my_app.py
261
+ from kalibr import Kalibr
262
+
263
+ app = Kalibr(title="Weather API")
264
+
265
+ @app.action("get_weather", "Get current weather")
266
+ def get_weather(city: str):
267
+ # Your logic here
268
+ return {"city": city, "temp": 72, "condition": "sunny"}
269
+ ```
270
+
271
+ ```bash
272
+ # Run it
273
+ kalibr-connect serve my_app.py
274
+
275
+ # Now ALL these work:
276
+ # ✅ ChatGPT can call get_weather()
277
+ # ✅ Claude can call get_weather()
278
+ # ✅ Gemini can call get_weather()
279
+ # ✅ Copilot can call get_weather()
280
+ ```
281
+
282
+ ---
283
+
284
+ ## 🚀 Get Started Now
285
+
286
+ ```bash
287
+ pip install kalibr
288
+ kalibr-connect examples
289
+ kalibr-connect serve kalibr_examples/basic_kalibr_example.py
290
+ ```
291
+
292
+ Open http://localhost:8000 and see your multi-model API in action! 🎯
293
+
294
+ ---
295
+
296
+ ## 📝 License
297
+
298
+ MIT License - see LICENSE file for details.
299
+
300
+ ---
301
+
302
+ **Kalibr SDK** - Transform how you build AI-integrated applications! 🚀
@@ -0,0 +1,16 @@
1
+ kalibr/__init__.py,sha256=1IIl43EB3tAsP1anJhi63PyD9Cx-cOGYPGJ9eKbAB0s,188
2
+ kalibr/__main__.py,sha256=Hl_-_vfimqHWZEwAUl4_OjrUWb1l7pM4q_13wbDI6gY,25584
3
+ kalibr/deployment.py,sha256=B-2ePPCMF2UcnkM2YKkAoaNrOjpUKUGfNO0IxyNOJMI,670
4
+ kalibr/kalibr.py,sha256=yrgXVlTgadBbpnX_l7fAxxjxGp9oxcZhzGjaQPiIcpo,10469
5
+ kalibr/kalibr_app.py,sha256=gNQprofiDk8CUk0oxC8kwRiOt32VkoRhKc2t6cgD3IA,19307
6
+ kalibr/schema_generators.py,sha256=nIgoYaO0FGC6arHdUHG0XaGDpJDycJeWDDC1-zAHzfI,7528
7
+ kalibr/types.py,sha256=bNmf_cOWXBmhaMVAPEp3_EdRCcdXY2pbOgOxZ1dZ0Mc,3476
8
+ kalibr-1.0.20.data/data/examples/README.md,sha256=loo2nm6yfT-pqGb5uNg1VeEdOKflYzHISUHTuSltfY0,4875
9
+ kalibr-1.0.20.data/data/examples/basic_kalibr_example.py,sha256=Kfrh-XZuJ0vwFLB_xBpdqpgpMJw2NpIx0yBsqrAqBnE,2188
10
+ kalibr-1.0.20.data/data/examples/enhanced_kalibr_example.py,sha256=AuhTpyRUNVAJuZKRy9iydXusNkBgQ84eKNiXxsr4iUQ,11994
11
+ kalibr-1.0.20.dist-info/licenses/LICENSE,sha256=1WLJDkrueNpHCROy9zANrK2Ar2weqZ_z88hw90UKDoc,451
12
+ kalibr-1.0.20.dist-info/METADATA,sha256=mKCIVFrYPVFhl2XUidHMI21O8_-L8VNVvarWmshJO8E,7965
13
+ kalibr-1.0.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ kalibr-1.0.20.dist-info/entry_points.txt,sha256=T-DOrFEZb0fZxA9H8sSCh-2zKxdjnmpzIRmm5TY_f6s,56
15
+ kalibr-1.0.20.dist-info/top_level.txt,sha256=OkloC5_IfpE4-QwI30aLIYbFZk_-ChABWF7aBGddy28,7
16
+ kalibr-1.0.20.dist-info/RECORD,,