Metadata-Version: 2.4
Name: qwen-reverse
Version: 0.1.2
Summary: Reverse-engineered Python client for Qwen web conversations (chat.qwen.ai) - text, vision, image & video generation
Author: Popbob
License-Expression: MIT
Project-URL: Homepage, https://github.com/AnonymoDGH/Qwen-Reverse
Project-URL: Repository, https://github.com/AnonymoDGH/Qwen-Reverse
Keywords: qwen,qwen3,chat.qwen.ai,reverse-engineered,api,chatgpt-api-wrapper,llm,ai-chat,image-generation
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: aiohttp>=3.10.0
Requires-Dist: pycryptodome>=3.20.0
Provides-Extra: server
Requires-Dist: fastapi>=0.115.0; extra == "server"
Requires-Dist: uvicorn[standard]>=0.30.0; extra == "server"
Requires-Dist: python-dotenv>=1.0.0; extra == "server"
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: build>=1.0.0; extra == "dev"
Dynamic: license-file

# Qwen-Reverse

[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
[![PyPI version](https://img.shields.io/pypi/v/qwen-reverse)](https://pypi.org/project/qwen-reverse/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.txt)

Reverse-engineered async Python client for [chat.qwen.ai](https://chat.qwen.ai) — text chat, streaming with real-time reasoning, tool calling, image and video generation. No official API key required (works anonymously).

```bash
pip install qwen-reverse
```

## Features

- **Chat** — one-shot, streaming (SSE incremental), multi-turn with conversation memory (`conversation_id` / `parent_id` chaining)
- **Real-time reasoning** — `reasoning` events streamed token-by-token before the answer, in both plain and multi-turn mode
- **Tool calling** — OpenAI-style function definitions; either let the SDK execute them (JSON-stringified follow-up) or handle them yourself with `emit_tool_calls=True`
- **Vision** — image upload + chat about it
- **Image / video generation** — t2i and t2v returning CDN URLs (`cdn.qwenlm.ai`)
- **No account required** — the web API works without a token; OAuth device-flow login (`chat.qwen.ai` account) is also implemented
- **Optional FastAPI server** — OpenAI-compatible `/v1/chat/completions` and `/v1/models` (see `server/`)

## Quickstart

```python
import asyncio
from qwen_reverse import Generative

async def main():
    gen = Generative()
    print(await gen.generate("Explain what a kernel is in 2 lines"))

asyncio.run(main())
```

### Streaming with real-time thinking

```python
import asyncio
from qwen_reverse import Generative

async def main():
    gen = Generative()
    async for event in gen.stream("Explain in 2 sentences what an OS kernel is"):
        if event["type"] == "reasoning":
            print(f"\r\033[90m{event['data']}\033[0m", end="", flush=True)
        elif event["type"] == "content":
            print(event["data"], end="", flush=True)

asyncio.run(main())
```

### Multi-turn conversation

```python
import asyncio
from qwen_reverse import Conversation

async def main():
    conv = Conversation()
    r1 = await conv.send("My name is Popbob and I work with kernels in C.")
    r2 = await conv.send("What is my name?")  # remembers turn 1
    print(r2)  # "Popbob"
    print(conv.conversation_id, conv.parent_id)

asyncio.run(main())
```

### Tool calling (automatic execution)

```python
import asyncio
from qwen_reverse import Conversation

WEATHER_TOOL = {
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}

async def main():
    conv = Conversation(tools=[WEATHER_TOOL])
    async for event in conv.stream("What's the weather in Buenos Aires?"):
        if event["type"] == "tool_calls":
            print("[tool_calls]", event["data"])
        elif event["type"] == "content":
            print(event["data"], end="", flush=True)

asyncio.run(main())
```

For raw tool-call JSON instead of execution, pass `emit_tool_calls=True` to `stream()`/`create_chat()`.

### Image generation

```python
import asyncio
from qwen_reverse import Image

async def main():
    img = Image()
    urls = await img.generate(
        "A cyberpunk dragon flying over a neon city, anime style",
        aspect_ratio="16:9",
    )
    print(urls[0])  # https://cdn.qwenlm.ai/output/...

asyncio.run(main())
```

## API

| Symbol | Description |
|---|---|
| `Generative(model=..., token=...)` | `.generate()`, `.stream()` (events: `reasoning`, `content`, `usage`, `tool_calls`, `done`) |
| `Conversation(token=..., tools=...)` | `.send()`, `.stream()`, `.reset()` — persists `conversation_id`/`parent_id` across turns |
| `Image(model=...)`, `Video(model=...)` | `.generate(prompt, aspect_ratio=...)` → list of CDN URLs |
| `create_chat(model, messages, ...)` | low-level async generator; `conversation_id`/`parent_id` for multi-turn |
| `fetch_models()` | available models (`qwen3.8-max`, `qwen3.7-plus`, `qwen3.7-max`, ...) |
| `upload_file(...)` | upload images/files for vision chats |
| `start_device_login()` / `complete_device_login(...)` | OAuth device flow |
| `QwenOAuth2Client`, `SharedTokenManager` | token management |
| `generate_cookies()` / `generate_fingerprint()` / `BXUAGenerator` | anti-bot primitives (cookies `ssxmod_itna`, `bx-umidtoken`, fingerprint) |

### Events emitted by `stream()`

| Event | Data | When |
|---|---|---|
| `reasoning` | `str` (chunk) | during `phase: "think"` — the model's chain of thought |
| `content` | `str` (chunk) | during `phase: "answer"` — the actual reply |
| `tool_calls` | `list[dict]` | when the model requests functions |
| `image` / `image_done` | `{"url", "extra"}` / `None` | image generation progress |
| `usage` | `dict` | token usage (`input_tokens`, `output_tokens`, ...) |
| `done` | — | final event; carries `conversation_id` and `parent_id` |

## Authentication

**Anonymous mode works** — no token required for most features (lower rate limits). For higher limits, log in with a `chat.qwen.ai` account:

```python
import asyncio
from qwen_reverse import start_device_login, complete_device_login

async def main():
    client, data = await start_device_login()
    print(data["verification_uri_complete"])  # open in a logged-in browser
    # after authorizing:
    token = await complete_device_login(client, data)
    print(token)
```

## Models

Defaults to `qwen3.8-max`. Others: `qwen3.7-plus`, `qwen3.7-max`, plus vision / image / video models — see `qwen_reverse/models.py` and `fetch_models()`.

## Running the tests

```bash
pip install -e ".[dev]"
pytest
```

## Optional server (OpenAI-compatible)

```bash
pip install "qwen-reverse[server]"
cd server
uvicorn app.main:app --port 8000
# GET  /v1/models
# POST /v1/chat/completions  (OpenAI-style, streaming SSE)
```

## How it works

The client replicates what the official web app does against `chat.qwen.ai`:

1. Create a chat via `POST /api/v2/chats/new` (gets a `chat_id`)
2. Stream the response via `POST /api/v2/chat/completions?chat_id=...` with `version: 2.1`, `incremental_output` and a `feature_config` that enables the reasoning stream
3. Multi-turn chaining uses the server's `response_id` (assistant message fid) as the next turn's `parent_id`
4. Anti-bot headers are regenerated per request: `ssxmod_itna` cookies (custom LZW + custom base64), `bx-umidtoken`, fingerprint

## Disclaimer

This project is for **educational and research purposes**. It is not affiliated with or endorsed by Alibaba/Qwen. Use at your own risk — the endpoints may change or the service may rate-limit or block unofficial clients. MIT licensed; reverse-engineering references based on [g4f](https://github.com/xtekky/gpt4free) (MIT).
