gitcast 1.0.24__tar.gz → 1.0.26__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.
- gitcast-1.0.26/.env.example +26 -0
- gitcast-1.0.26/PKG-INFO +183 -0
- gitcast-1.0.26/README.md +142 -0
- gitcast-1.0.26/ai/generator.py +165 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/routes.py +157 -187
- {gitcast-1.0.24 → gitcast-1.0.26}/api/server.py +4 -6
- gitcast-1.0.26/cli/.env.example +26 -0
- gitcast-1.0.26/cli/gitcast.py +221 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/config/settings.py +21 -81
- gitcast-1.0.26/core/cloud_client.py +163 -0
- gitcast-1.0.26/gitcast.egg-info/PKG-INFO +183 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/gitcast.egg-info/SOURCES.txt +1 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/gitcast.egg-info/requires.txt +1 -3
- {gitcast-1.0.24 → gitcast-1.0.26}/requirements.txt +5 -7
- {gitcast-1.0.24 → gitcast-1.0.26}/setup.py +2 -3
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/logger.py +68 -33
- gitcast-1.0.26/tests/test_keys.py +30 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/web/index.html +185 -190
- gitcast-1.0.24/.env.example +0 -40
- gitcast-1.0.24/PKG-INFO +0 -108
- gitcast-1.0.24/README.md +0 -65
- gitcast-1.0.24/ai/generator.py +0 -612
- gitcast-1.0.24/cli/.env.example +0 -40
- gitcast-1.0.24/cli/gitcast.py +0 -132
- gitcast-1.0.24/gitcast.egg-info/PKG-INFO +0 -108
- gitcast-1.0.24/tests/test_keys.py +0 -86
- {gitcast-1.0.24 → gitcast-1.0.26}/LICENSE +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/MANIFEST.in +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/ai/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/ai/formatter.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/ai/prompts.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/ai/viral_patterns.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/analytics.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/auth.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/auth_middleware.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/auth_routes.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/monitoring.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/payload.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/ratelimit.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/api/validators.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/assets/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/assets/favicon-16x16.png +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/assets/favicon-32x32.png +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/assets/favicon-64x64.png +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/assets/favicon.ico +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/assets/icon.png +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/cli/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/config/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/capture.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/codebase_reader.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/framing.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/hotkey.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/log_stream.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/ocr.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/screenshot_session.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/security.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/tray.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/core/trigger.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/api-keys.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/article-generator.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/capture-flow.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/changelog.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/contributing.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/dashboard.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/insights.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/introduction.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/post-formats.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/providers.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/pypi-errors.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/quickstart.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/docs/sprint-mode.md +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/gitcast.egg-info/dependency_links.txt +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/gitcast.egg-info/entry_points.txt +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/gitcast.egg-info/top_level.txt +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/publisher/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/publisher/clipboard.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/publisher/twitter.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/pyproject.toml +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/setup.cfg +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/cleanup.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/data/prompts.json +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/engagement.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/insights.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/key_manager.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/metrics.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/sprint.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/streak.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/supabase_client.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/storage/tone_memory.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/tests/test_ocr_fix.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/tests/test_validation.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/ui/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/ui/popup.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/ui/review.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/web/__init__.py +0 -0
- {gitcast-1.0.24 → gitcast-1.0.26}/web/landing.html +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ────────────────────────────────────────────
|
|
2
|
+
# GITCAST CONFIGURATION
|
|
3
|
+
# ────────────────────────────────────────────
|
|
4
|
+
# Gitcast works out of the box — no keys needed.
|
|
5
|
+
# The shared key is built in.
|
|
6
|
+
#
|
|
7
|
+
# Add your own key only if you want unlimited
|
|
8
|
+
# personal usage without shared rate limits.
|
|
9
|
+
# ────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
# Your optional API key (BYOK)
|
|
12
|
+
# Get free at console.groq.com (2 minutes)
|
|
13
|
+
# Leave empty to use Gitcast shared key
|
|
14
|
+
BYOK_KEY=
|
|
15
|
+
BYOK_PROVIDER=groq
|
|
16
|
+
|
|
17
|
+
# Twitter — only needed for direct X publishing
|
|
18
|
+
# Get from developer.twitter.com
|
|
19
|
+
TWITTER_API_KEY=
|
|
20
|
+
TWITTER_API_SECRET=
|
|
21
|
+
TWITTER_ACCESS_TOKEN=
|
|
22
|
+
TWITTER_ACCESS_SECRET=
|
|
23
|
+
TWITTER_BEARER_TOKEN=
|
|
24
|
+
|
|
25
|
+
# Cloud server (do not change)
|
|
26
|
+
GITCAST_API_URL=https://gitcast-api.onrender.com
|
gitcast-1.0.26/PKG-INFO
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gitcast
|
|
3
|
+
Version: 1.0.26
|
|
4
|
+
Summary: git diff → published post. under 60 seconds.
|
|
5
|
+
Home-page: https://github.com/drizzy765/gitcast
|
|
6
|
+
Author: Timilehin Agoro
|
|
7
|
+
Author-email: agorotimilehi05@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: pynput
|
|
13
|
+
Requires-Dist: pystray
|
|
14
|
+
Requires-Dist: Pillow
|
|
15
|
+
Requires-Dist: fastapi
|
|
16
|
+
Requires-Dist: uvicorn
|
|
17
|
+
Requires-Dist: python-dotenv
|
|
18
|
+
Requires-Dist: tweepy
|
|
19
|
+
Requires-Dist: mss
|
|
20
|
+
Requires-Dist: pytesseract
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Requires-Dist: cryptography
|
|
23
|
+
Requires-Dist: python-jose[cryptography]
|
|
24
|
+
Requires-Dist: rich
|
|
25
|
+
Requires-Dist: keyboard
|
|
26
|
+
Requires-Dist: slowapi
|
|
27
|
+
Requires-Dist: limits
|
|
28
|
+
Requires-Dist: sentry-sdk[fastapi]
|
|
29
|
+
Requires-Dist: posthog
|
|
30
|
+
Requires-Dist: plyer
|
|
31
|
+
Dynamic: author
|
|
32
|
+
Dynamic: author-email
|
|
33
|
+
Dynamic: description
|
|
34
|
+
Dynamic: description-content-type
|
|
35
|
+
Dynamic: home-page
|
|
36
|
+
Dynamic: license
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
41
|
+
|
|
42
|
+
# Gitcast
|
|
43
|
+
|
|
44
|
+
> git diff -> published post. under 60 seconds.
|
|
45
|
+
|
|
46
|
+
Privacy-first developer tool that captures your coding session via hotkey, generates AI-powered social media posts, and publishes them without breaking your flow.
|
|
47
|
+
|
|
48
|
+
Works immediately after install - no API key setup required. Gitcast's shared key is built in.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
### Step 1 - Install Tesseract OCR
|
|
55
|
+
|
|
56
|
+
Required for local screenshot text extraction. Runs entirely on your machine - your code never leaves without your permission.
|
|
57
|
+
|
|
58
|
+
**Windows:**
|
|
59
|
+
|
|
60
|
+
1. Download: github.com/UB-Mannheim/tesseract/wiki
|
|
61
|
+
2. Run installer
|
|
62
|
+
3. Check "Add to PATH" during installation
|
|
63
|
+
4. Restart your terminal
|
|
64
|
+
|
|
65
|
+
**Mac:**
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
brew install tesseract
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Linux:**
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
sudo apt install tesseract-ocr
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Step 2 - Install Gitcast
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install gitcast
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Step 3 - Run
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
gitcast
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Dashboard opens automatically at http://localhost:8000
|
|
90
|
+
|
|
91
|
+
Press **Ctrl+Shift+P** or **Ctrl+Alt+S** from anywhere - VS Code, terminal, browser - to capture your screen and generate a post.
|
|
92
|
+
|
|
93
|
+
Works immediately. No configuration needed. Gitcast's shared API key is built in.
|
|
94
|
+
|
|
95
|
+
### Step 4 - Add your own key (optional)
|
|
96
|
+
|
|
97
|
+
The shared key has rate limits shared across users. For unlimited personal usage, add your own free key:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
gitcast --setup
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Or go to **[BYOK]** in the dashboard sidebar.
|
|
104
|
+
|
|
105
|
+
Get a free Groq key in 2 minutes at console.groq.com
|
|
106
|
+
|
|
107
|
+
Your key takes priority - shared key stays as fallback.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## What Gitcast generates
|
|
112
|
+
|
|
113
|
+
From a single hotkey press:
|
|
114
|
+
|
|
115
|
+
- **X (Twitter) post** - published directly via API
|
|
116
|
+
- **LinkedIn draft** - with framed screenshot
|
|
117
|
+
- **PR description** - paste into GitHub
|
|
118
|
+
- **Quick win** - short punchy update
|
|
119
|
+
- **Article** - Medium-ready markdown
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## How it works
|
|
124
|
+
|
|
125
|
+
```text
|
|
126
|
+
Ctrl+Shift+P (or Ctrl+Alt+S)
|
|
127
|
+
|
|
128
|
+
-> screenshot captured locally
|
|
129
|
+
|
|
130
|
+
-> git diff read locally
|
|
131
|
+
|
|
132
|
+
-> OCR runs on your machine (Tesseract)
|
|
133
|
+
|
|
134
|
+
-> payload sent to Gitcast cloud
|
|
135
|
+
|
|
136
|
+
-> AI generates 4 post variations
|
|
137
|
+
|
|
138
|
+
-> dashboard shows results
|
|
139
|
+
|
|
140
|
+
-> you review, refine, publish
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Privacy
|
|
146
|
+
|
|
147
|
+
- OCR runs locally - text extracted on your machine
|
|
148
|
+
- Screenshots processed locally before any upload
|
|
149
|
+
- Sensitive content (API keys, tokens) auto-blocked
|
|
150
|
+
- Nothing always-on - you trigger every capture
|
|
151
|
+
- No account required to use
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## CLI
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
gitcast Start dashboard + hotkey
|
|
159
|
+
|
|
160
|
+
gitcast --setup Add your own API key (optional)
|
|
161
|
+
|
|
162
|
+
gitcast capture Interactive screenshot session
|
|
163
|
+
|
|
164
|
+
gitcast --version Show version
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Stack
|
|
170
|
+
|
|
171
|
+
- Python 3.10+, FastAPI, pynput, mss
|
|
172
|
+
- Tesseract OCR (local)
|
|
173
|
+
- Groq, Gemini, OpenRouter, Cerebras (cloud)
|
|
174
|
+
- Tweepy (X API v2)
|
|
175
|
+
- Supabase (cloud storage)
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT - free forever. Open source.
|
|
182
|
+
|
|
183
|
+
Built by Timilehin Agoro (@drizzy765) in Nigeria.
|
gitcast-1.0.26/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Gitcast
|
|
2
|
+
|
|
3
|
+
> git diff -> published post. under 60 seconds.
|
|
4
|
+
|
|
5
|
+
Privacy-first developer tool that captures your coding session via hotkey, generates AI-powered social media posts, and publishes them without breaking your flow.
|
|
6
|
+
|
|
7
|
+
Works immediately after install - no API key setup required. Gitcast's shared key is built in.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### Step 1 - Install Tesseract OCR
|
|
14
|
+
|
|
15
|
+
Required for local screenshot text extraction. Runs entirely on your machine - your code never leaves without your permission.
|
|
16
|
+
|
|
17
|
+
**Windows:**
|
|
18
|
+
|
|
19
|
+
1. Download: github.com/UB-Mannheim/tesseract/wiki
|
|
20
|
+
2. Run installer
|
|
21
|
+
3. Check "Add to PATH" during installation
|
|
22
|
+
4. Restart your terminal
|
|
23
|
+
|
|
24
|
+
**Mac:**
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
brew install tesseract
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Linux:**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
sudo apt install tesseract-ocr
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Step 2 - Install Gitcast
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install gitcast
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Step 3 - Run
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
gitcast
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Dashboard opens automatically at http://localhost:8000
|
|
49
|
+
|
|
50
|
+
Press **Ctrl+Shift+P** or **Ctrl+Alt+S** from anywhere - VS Code, terminal, browser - to capture your screen and generate a post.
|
|
51
|
+
|
|
52
|
+
Works immediately. No configuration needed. Gitcast's shared API key is built in.
|
|
53
|
+
|
|
54
|
+
### Step 4 - Add your own key (optional)
|
|
55
|
+
|
|
56
|
+
The shared key has rate limits shared across users. For unlimited personal usage, add your own free key:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
gitcast --setup
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or go to **[BYOK]** in the dashboard sidebar.
|
|
63
|
+
|
|
64
|
+
Get a free Groq key in 2 minutes at console.groq.com
|
|
65
|
+
|
|
66
|
+
Your key takes priority - shared key stays as fallback.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## What Gitcast generates
|
|
71
|
+
|
|
72
|
+
From a single hotkey press:
|
|
73
|
+
|
|
74
|
+
- **X (Twitter) post** - published directly via API
|
|
75
|
+
- **LinkedIn draft** - with framed screenshot
|
|
76
|
+
- **PR description** - paste into GitHub
|
|
77
|
+
- **Quick win** - short punchy update
|
|
78
|
+
- **Article** - Medium-ready markdown
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## How it works
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
Ctrl+Shift+P (or Ctrl+Alt+S)
|
|
86
|
+
|
|
87
|
+
-> screenshot captured locally
|
|
88
|
+
|
|
89
|
+
-> git diff read locally
|
|
90
|
+
|
|
91
|
+
-> OCR runs on your machine (Tesseract)
|
|
92
|
+
|
|
93
|
+
-> payload sent to Gitcast cloud
|
|
94
|
+
|
|
95
|
+
-> AI generates 4 post variations
|
|
96
|
+
|
|
97
|
+
-> dashboard shows results
|
|
98
|
+
|
|
99
|
+
-> you review, refine, publish
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Privacy
|
|
105
|
+
|
|
106
|
+
- OCR runs locally - text extracted on your machine
|
|
107
|
+
- Screenshots processed locally before any upload
|
|
108
|
+
- Sensitive content (API keys, tokens) auto-blocked
|
|
109
|
+
- Nothing always-on - you trigger every capture
|
|
110
|
+
- No account required to use
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## CLI
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
gitcast Start dashboard + hotkey
|
|
118
|
+
|
|
119
|
+
gitcast --setup Add your own API key (optional)
|
|
120
|
+
|
|
121
|
+
gitcast capture Interactive screenshot session
|
|
122
|
+
|
|
123
|
+
gitcast --version Show version
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Stack
|
|
129
|
+
|
|
130
|
+
- Python 3.10+, FastAPI, pynput, mss
|
|
131
|
+
- Tesseract OCR (local)
|
|
132
|
+
- Groq, Gemini, OpenRouter, Cerebras (cloud)
|
|
133
|
+
- Tweepy (X API v2)
|
|
134
|
+
- Supabase (cloud storage)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
MIT - free forever. Open source.
|
|
141
|
+
|
|
142
|
+
Built by Timilehin Agoro (@drizzy765) in Nigeria.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
from ai.prompts import sprint_summary_prompt
|
|
3
|
+
from core.log_stream import stream_log
|
|
4
|
+
|
|
5
|
+
# ── Constants ─────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
TIMEOUT = 15
|
|
8
|
+
|
|
9
|
+
_last_call_meta = {"provider_used": "", "used_fallback": False}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def refresh_provider_keys(user_id: str = "") -> None:
|
|
13
|
+
from config.settings import reload_api_keys
|
|
14
|
+
|
|
15
|
+
reload_api_keys()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ── Main generator ────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
async def generate_posts(payload: dict, user_id: str = "") -> dict:
|
|
21
|
+
from core.cloud_client import cloud_generate
|
|
22
|
+
|
|
23
|
+
stream_log("GENERATOR", "INFO", "sending to Gitcast cloud server...")
|
|
24
|
+
|
|
25
|
+
results = await cloud_generate(payload)
|
|
26
|
+
|
|
27
|
+
if not results:
|
|
28
|
+
stream_log("GENERATOR", "ERROR", "cloud server returned no results")
|
|
29
|
+
return {
|
|
30
|
+
k: (
|
|
31
|
+
"[Error] Could not reach generation "
|
|
32
|
+
"server. Check your internet connection."
|
|
33
|
+
)
|
|
34
|
+
for k in payload.get(
|
|
35
|
+
"format_keys",
|
|
36
|
+
["x_post", "linkedin", "pr_desc", "quick_win"],
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for key in results:
|
|
41
|
+
if isinstance(results[key], str) and not results[key].startswith("[Error]"):
|
|
42
|
+
stream_log("GENERATOR", "OK", f"{key} generated successfully")
|
|
43
|
+
|
|
44
|
+
return results
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def refine_post(
|
|
48
|
+
current_post: str,
|
|
49
|
+
instruction: str,
|
|
50
|
+
format_key: str,
|
|
51
|
+
**kwargs,
|
|
52
|
+
) -> str:
|
|
53
|
+
from core.cloud_client import cloud_refine
|
|
54
|
+
|
|
55
|
+
return await cloud_refine(
|
|
56
|
+
current_post=current_post,
|
|
57
|
+
instruction=instruction,
|
|
58
|
+
format_key=format_key,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ── Sprint generator ──────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
async def generate_sprint_summary(entries: list, narrative: str = "", user_id: str = "") -> str:
|
|
65
|
+
"""
|
|
66
|
+
Synthesizes multiple sprint captures into a single cohesive thread.
|
|
67
|
+
"""
|
|
68
|
+
from api.payload import build_sprint_payload
|
|
69
|
+
|
|
70
|
+
stream_log("Generator", "AI", f"synthesizing {len(entries)} captures into sprint thread")
|
|
71
|
+
|
|
72
|
+
payload = build_sprint_payload(entries)
|
|
73
|
+
if narrative:
|
|
74
|
+
payload["narrative"] = narrative
|
|
75
|
+
system_prompt = sprint_summary_prompt(len(entries))
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
return await _ai_call("sprint_summary", system_prompt, payload["user_message"], user_id=user_id)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
stream_log("Generator", "ERROR", f"sprint synthesis failed: {e}")
|
|
81
|
+
return f"[Error] {str(e)}"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ── Provider Routing & Fallback ────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
async def _ai_call(
|
|
87
|
+
format_key: str,
|
|
88
|
+
system_prompt: str,
|
|
89
|
+
user_message: str,
|
|
90
|
+
user_id: str = "",
|
|
91
|
+
unavailable: dict[str, str] | None = None,
|
|
92
|
+
client: httpx.AsyncClient = None,
|
|
93
|
+
meta: dict | None = None,
|
|
94
|
+
) -> str:
|
|
95
|
+
"""Compatibility wrapper that routes legacy route calls through the cloud."""
|
|
96
|
+
results = await generate_posts(
|
|
97
|
+
{
|
|
98
|
+
"format_keys": [format_key],
|
|
99
|
+
"system_prompt": system_prompt,
|
|
100
|
+
"user_message": user_message,
|
|
101
|
+
},
|
|
102
|
+
user_id=user_id,
|
|
103
|
+
)
|
|
104
|
+
content = results.get(format_key, "")
|
|
105
|
+
if not content:
|
|
106
|
+
return "[Error] Could not reach generation server. Check your internet connection."
|
|
107
|
+
return content
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def _groq_call(system_prompt: str, user_message: str, retries: int = 2) -> str:
|
|
111
|
+
return await _ai_call("deep_tech", system_prompt, user_message)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def _gemini_text_call(system_prompt: str, user_message: str, client: httpx.AsyncClient = None) -> str:
|
|
115
|
+
return await _ai_call("deep_tech", system_prompt, user_message)
|
|
116
|
+
|
|
117
|
+
async def _gemini_vision_call(
|
|
118
|
+
system_prompt: str,
|
|
119
|
+
user_message: str,
|
|
120
|
+
screenshots: list,
|
|
121
|
+
client: httpx.AsyncClient = None,
|
|
122
|
+
) -> str:
|
|
123
|
+
return await _ai_call("deep_tech", system_prompt, user_message)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _encode_image(image_path: str) -> str:
|
|
127
|
+
import base64
|
|
128
|
+
try:
|
|
129
|
+
with open(image_path, "rb") as f:
|
|
130
|
+
return base64.b64encode(f.read()).decode("utf-8")
|
|
131
|
+
except Exception:
|
|
132
|
+
return ""
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ── Test ──────────────────────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
import asyncio
|
|
139
|
+
from core.capture import run_capture
|
|
140
|
+
from core.ocr import run_ocr
|
|
141
|
+
from api.payload import build_payload
|
|
142
|
+
from config.settings import set_project_narrative
|
|
143
|
+
|
|
144
|
+
set_project_narrative("an AI-powered build-in-public automation tool for developers")
|
|
145
|
+
|
|
146
|
+
async def test():
|
|
147
|
+
print("[Generator] Running multi-provider routing test...")
|
|
148
|
+
capture = run_capture()
|
|
149
|
+
ocr = run_ocr(capture["screenshot"]["path"])
|
|
150
|
+
payload = build_payload(
|
|
151
|
+
raw_thought="testing the new multi-provider fallback chain with Cerebras and OpenRouter",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
payload["use_vision_fallback"] = False
|
|
155
|
+
payload["screenshot_b64"] = None
|
|
156
|
+
|
|
157
|
+
print("[Generator] Firing routed AI calls...")
|
|
158
|
+
results = await generate_posts(payload)
|
|
159
|
+
|
|
160
|
+
print("\n=== GENERATED POSTS ===")
|
|
161
|
+
for format_key, content in results.items():
|
|
162
|
+
print(f"\n--- {format_key.upper()} ---")
|
|
163
|
+
print(content)
|
|
164
|
+
|
|
165
|
+
asyncio.run(test())
|