Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ lib/
lib64/
parts/
sdist/
dist/
var/
wheels/
*.egg-info/
Expand Down Expand Up @@ -54,3 +55,6 @@ logs/

# ChatGPT session artifacts
sessions.json

# Graphify (derived code graph, regenerate with graphify update .)
graphify-out/
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Ponytail, lazy senior dev mode

You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.

Before writing any code, stop at the first rung that holds:

1. Does this need to be built at all? (YAGNI)
2. Does the standard library already do this? Use it.
3. Does a native platform feature cover it? Use it.
4. Does an already-installed dependency solve it? Use it.
5. Can this be one line? Make it one line.
6. Only then: write the minimum code that works.

Rules:
- No abstractions that weren't explicitly requested.
- No new dependency if it can be avoided.
- No boilerplate nobody asked for.
- Deletion over addition. Boring over clever. Fewest files possible.
- Question complex requests: "Do you actually need X, or does Y cover it?"
- Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm.
- Mark intentional simplifications with a `ponytail:` comment. If the shortcut has a known ceiling (global lock, O(n²) scan, naive heuristic), the comment names the ceiling and the upgrade path.

Not lazy about: input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.

(Yes, this file also applies to agents working on the ponytail repo itself. Especially to them.)
304 changes: 30 additions & 274 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,289 +2,45 @@

# TextGenHub

My personal text generation hub for connecting to web-based LLMs in an automated manner. The package is available for both Python and Node.js environments, allowing flexible integration into various projects.
A text generation hub for connecting to various LLMs — web-based browsers, local models, and cloud APIs — from Python or Node.js.

It consists of:
## Supported Providers

- **Node.js backend** – handles direct interactions with LLMs using Puppeteer and related tools.
- **Python wrapper** – allows seamless integration into Python applications and agents.
| Provider | Type | API Key | Node.js |
|---|---|---|---|
| [ChatGPT](docs/python.md#chatgpt) | Web UI (browser) | OpenAI account | Yes |
| [DeepSeek](docs/python.md#web-ui-providers) | Web UI (browser) | DeepSeek account | Yes |
| [Perplexity](docs/python.md#web-ui-providers) | Web UI (browser) | Perplexity account | Yes |
| [Grok](docs/python.md#web-ui-providers) | Web UI (browser) | X/Twitter account | Yes |
| [Ollama](docs/python.md#ollama) | Local REST | None | No |
| [DeepSeek API](docs/python.md#deepseek-api) | Cloud REST | DeepSeek key | No |

## Supported LLMs
## Quick Start

- **ChatGPT** - OpenAI's ChatGPT via web interface
- **DeepSeek** - DeepSeek Chat via web interface (https://chat-deep.ai/deepseek-chat/)
- **Perplexity** - Perplexity AI via web interface (https://www.perplexity.ai/)
- **Grok** - Grok (X.com) via web interface (https://grok.com/)
```powershell
# 1. Install everything (Python deps, Node deps, optionally Ollama + dev tools)
.\setup.ps1

## Development Notes

> ⚠️ **Important**: This project maintains two `package.json` files:
> - `./package.json` - For npm package installation
> - `./src/textgenhub/package.json` - For Python package dependencies
>
> When modifying Node.js dependencies or version numbers, please ensure to update both files to keep them synchronized.



### Python Package
```bash
# Using pip
pip install textgenhub

# Using poetry
poetry add textgenhub
```

### Node.js Package
```bash
# Using npm
npm install textgenhub

# Using yarn
yarn add textgenhub
```

## Usage

### Python

All providers now support a unified `ask()` interface for consistency:

```python
from textgenhub import chatgpt, deepseek, perplexity

# Unified interface - all providers support ask()
# By default, prompts are pasted instantly (typing_speed=None)
response = chatgpt.ask("What is Python?", headless=True)
response = deepseek.ask("What is Python?", headless=True)
response = perplexity.ask("What is Python?", headless=True)

# For character-by-character typing, set typing_speed (in seconds per character)
response = chatgpt.ask("What is Python?", typing_speed=0.05)
```

#### ChatGPT
```python
from textgenhub import chatgpt

# Use the unified ask() interface
response = chatgpt.ask("What day is it today?", headless=True)
print(response)
```

#### DeepSeek
```python
from textgenhub import deepseek

# Use the unified ask() interface
response = deepseek.ask("What day is it today?", headless=True)
print(response)
# 2. Try it
poetry run textgenhub ollama --prompt "Hello, world!"
```

#### Perplexity
```python
from textgenhub import perplexity

# Use the unified ask() interface
response = perplexity.ask("What day is it today?", headless=True)
print(response)
```

### Node.js

#### ChatGPT
```javascript
const { ChatGPT } = require('textgenhub');

// Create a ChatGPT instance
const chatgpt = new ChatGPT();

// Use it in your code
chatgpt.chat("What day is it today?", { headless: true })
.then(response => console.log(response));
```

#### DeepSeek
```javascript
const { DeepSeek } = require('textgenhub');

// Create a DeepSeek instance
const deepseek = new DeepSeek();

// Use it in your code
deepseek.chat("What day is it today?", { headless: true })
.then(response => console.log(response));
```

#### Perplexity
```javascript
const { Perplexity } = require('textgenhub');

// Create a Perplexity instance
const perplexity = new Perplexity();

// Use it in your code
perplexity.chat("What day is it today?", { headless: true })
.then(response => console.log(response));
```

## Running the CLI

### Unified CLI Interface

TextGenHub now provides a unified CLI interface for all providers:

```bash
# Install and use the unified CLI
poetry install
poetry run textgenhub --help

# ChatGPT
poetry run textgenhub chatgpt --prompt "What day is it today?"

# DeepSeek (headless browser method)
poetry run textgenhub deepseek --prompt "What day is it today?"
## Switch Providers

# Perplexity (headless browser method)
poetry run textgenhub perplexity --prompt "What day is it today?"
Same CLI, different provider:

# Grok (headless browser method)
poetry run textgenhub grok --prompt "What day is it today?"
```powershell
poetry run textgenhub ollama --prompt "hi"                         # local model
poetry run textgenhub deepseek-api --prompt "hi"                   # cloud API
poetry run textgenhub chatgpt --prompt "hi"                        # browser automation
```

#### CLI Options
## Docs

- `--prompt`: The text prompt to send to the LLM (required for most providers)
- `--headless`: Run browser in headless mode (default: true)
- `--output-format`: Output format - `json` (default), `html`, or `raw` (ChatGPT: json/html/raw; others: json/html)
- `--timeout`: Timeout in seconds for extension mode (ChatGPT only, default: 120)
- `--typing-speed`: Typing speed in seconds per character (default: None for instant paste, > 0 for character-by-character typing)
- `--session`: Explicit session index to use (ChatGPT only, see `sessions list` command)
- `--close`: Close browser session after completion (ChatGPT only, default: keep open)

#### Session management (ChatGPT only)

```bash
# List all available ChatGPT sessions
poetry run textgenhub sessions list

# Show the path to the central sessions.json file
poetry run textgenhub sessions path

# Create a new ChatGPT session with auto-assigned index (opens browser for login)
poetry run textgenhub sessions init

# Create or regenerate a specific session index (opens browser for login)
poetry run textgenhub sessions init --index 0
poetry run textgenhub sessions init --index 2

# Get help on available session commands
poetry run textgenhub sessions --help
poetry run textgenhub sessions init --help
```

The ChatGPT provider supports browser profile isolation with intelligent session management. Sessions maintain conversation continuity and can be explicitly targeted with `--session INDEX`.

### Recovering or Moving Sessions

If you lose your sessions, move to a new device, or need to recreate your environment, follow these steps to rebuild your session library:

1. **(Optional) Clear existing corrupted sessions**:
If your sessions are in a bad state, you can start fresh by removing the central sessions file:
```bash
# On Windows (PowerShell)
powershell -Command "Remove-Item (poetry run textgenhub sessions path)"

# On Linux/macOS
rm $(poetry run textgenhub sessions path)
```

2. **Re-initialize specific sessions**:
Run the `init` command for each session index you wish to restore. This will open a browser window for you to log in:
```bash
# Initialize session 0 (the default session)
poetry run textgenhub sessions init --index 0

# Initialize additional sessions if needed
poetry run textgenhub sessions init --index 1
poetry run textgenhub sessions init --index 2
```

3. **Verify the rebuild**:
```bash
poetry run textgenhub sessions list
```

**Session Storage Policy**: `sessions.json` is stored centrally on your system to ensure consistency across all projects using `textgenhub`: `%LOCALAPPDATA%\textgenhub\sessions.json`.

This central location prevents the need to copy `sessions.json` between projects or virtual environments. If a local `sessions.json` exists in your project directory, it will be automatically migrated to the central location on first use.

#### CLI examples

```bash
# ChatGPT - JSON output (default)
poetry run textgenhub chatgpt --prompt "Explain quantum computing"

# ChatGPT with session-based provider - HTML output
poetry run textgenhub chatgpt --prompt "Explain quantum computing" --output-format html

# ChatGPT with session-based provider - Raw text output
poetry run textgenhub chatgpt --prompt "Explain quantum computing" --output-format raw

# ChatGPT with character-by-character typing (0.05 seconds per character)
poetry run textgenhub chatgpt --prompt "Explain quantum computing" --typing-speed 0.05

# ChatGPT using specific session (session index 1)
poetry run textgenhub chatgpt --prompt "Explain quantum computing" --session 1

# Regenerate session 0 if it's broken
poetry run textgenhub sessions init --index 0

# ChatGPT with automatic closing after receiving the response
poetry run textgenhub chatgpt --prompt "Explain quantum computing" --close

# Closing doesn't necessarily need a prompt, we can close a specific session as well
poetry run textgenhub chatgpt --close --session 0

# DeepSeek - JSON output
poetry run textgenhub deepseek --prompt "What is machine learning?"

# Perplexity - JSON output
poetry run textgenhub perplexity --prompt "What is the capital of France?"

# Grok - JSON output
poetry run textgenhub grok --prompt "Tell me a joke"
```

#### JSON Output Format

When using `--output-format json` (default), the CLI returns structured JSON:

```json
{
"provider": "chatgpt",
"method": "headless",
"timestamp": "2025-11-13T20:14:44.465890",
"prompt": "What is 2 + 2?",
"response": "2 + 2 equals 4.",
"html": ""
}
```

#### HTML Output Format

When using `--output-format html`, the CLI returns raw HTML content directly, perfect for downstream processing:

```bash
# Get HTML content for further processing
HTML_CONTENT=$(poetry run textgenhub chatgpt --prompt "Generate a report" --output-format html)
```

#### Raw Output Format

When using `--output-format raw` (ChatGPT session-based provider only), the CLI returns plain text content without any formatting or metadata:

```bash
# Get plain text response only
poetry run textgenhub chatgpt --prompt "Summarize the Ukraine crisis" --output-format raw
```
| Topic | File |
|---|---|
| Python usage (all providers) | [docs/python.md](docs/python.md) |
| Node.js usage | [docs/nodejs.md](docs/nodejs.md) |
| CLI reference (flags, examples) | [docs/cli.md](docs/cli.md) |
| ChatGPT session management | [docs/sessions.md](docs/sessions.md) |
| Development & architecture | [docs/development.md](docs/development.md) |
Loading
Loading