Skip to content
This repository was archived by the owner on Mar 21, 2024. It is now read-only.

Commit 9578d7f

Browse files
committed
added:
- GenerateQuery support - multimodal demo - kick support
1 parent ac8c867 commit 9578d7f

File tree

9 files changed

+168
-18
lines changed

9 files changed

+168
-18
lines changed

README.md

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ This is an unofficial PHP client for **Bing AI**, including **Chat (GPT-4)** and
1313

1414
composer require maximerenou/bing-ai
1515

16+
## Demo
17+
18+
This demo program uses both Chat and Image Creator:
19+
20+
![Demo](examples/demo.gif)
21+
22+
Source: `examples/multi.php`.
23+
1624
## Usage
1725

1826
- [Chat AI](#chat-ai)
@@ -50,9 +58,7 @@ $valid = $ai->checkCookie();
5058

5159
### Chat AI
5260

53-
![Chat demo](examples/demo-chat.gif)
54-
55-
**Demo**: clone this repo, edit and run `examples/chat.php` to test it.
61+
**Demo**: clone this repo, edit and run `examples/chat.php`.
5662

5763
```php
5864
use MaximeRenou\BingAI\BingAI;
@@ -64,11 +70,11 @@ $ai = new BingAI($cookie);
6470
$conversation = $ai->createChatConversation();
6571

6672
// $text - Text-only version of Bing's answer
67-
// $messages - Message objects array
68-
list($text, $messages) = $conversation->ask(new Prompt("Hello World"));
73+
// $cards - Message objects array
74+
list($text, $cards) = $conversation->ask(new Prompt("Hello World"));
6975
```
7076

71-
> Every "card" from Bing AI is fetched. Check `Message.php` to learn more
77+
> `$cards` contains all "messages" exchanged with Bing AI. It can be text (prompt or answer), signals, suggestions, image generation requests, etc. Check `Message.php` to learn more
7278
about its format.
7379

7480
<details>
@@ -78,8 +84,8 @@ You may pass a function as second argument to get real-time progression:
7884

7985
```php
8086
// $text - Incomplete text version
81-
// $messages - Incomplete messages fleet
82-
list($final_text, $final_messages) = $conversation->ask($prompt, function ($text, $messages) {
87+
// $cards - Incomplete messages fleet
88+
list($final_text, $final_cards) = $conversation->ask($prompt, function ($text, $cards) {
8389
echo $text;
8490
});
8591
```
@@ -149,7 +155,7 @@ $conversation->ask($prompt->withoutCache(), ...)
149155
</details>
150156

151157
<details>
152-
<summary>Throttling</summary>
158+
<summary>Handle throttling and kicks</summary>
153159

154160
Bing is limiting messages count per conversations. You can monitor it by calling `getRemainingMessages()` after every interaction.
155161

@@ -161,15 +167,29 @@ if ($remaining === 0) {
161167
}
162168
```
163169

170+
After every interaction, you should also check if you have been kicked from the conversation:
171+
172+
```php
173+
if ($conversation->kicked()) {
174+
// You have been kicked, you should start a new conversation
175+
}
176+
```
177+
178+
You may combine both checks with:
179+
180+
```php
181+
if ($conversation->ended()) {
182+
// You reached the limit or have been kicked
183+
}
184+
```
185+
164186
</details>
165187

166188
---------------------------------------
167189

168190
### Image Creator
169191

170-
![Image Creator demo](examples/demo-images.gif)
171-
172-
**Demo**: clone this repo, edit and run `examples/images.php` to test it.
192+
**Demo**: clone this repo, edit and run `examples/images.php`.
173193

174194
```php
175195
use MaximeRenou\BingAI\BingAI;

examples/chat.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
// Print partial answer
3838
echo "- $text";
39-
$padding = mb_strlen($text) + 2;
39+
$padding = strlen($text) + 2;
4040
});
4141

4242
// Erase the last line
@@ -46,6 +46,11 @@
4646
// Print final answer
4747
echo "- $text" . PHP_EOL;
4848

49+
if ($conversation->kicked()) {
50+
echo "[Conversation ended]" . PHP_EOL;
51+
break;
52+
}
53+
4954
$remaining = $conversation->getRemainingMessages();
5055

5156
if ($remaining != 0) {

examples/demo-chat.gif

-76.6 KB
Binary file not shown.

examples/demo-images.gif

-1.32 MB
Binary file not shown.

examples/demo.gif

738 KB
Loading

examples/multi.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
$cookie = "YOUR_COOKIE_HERE"; //@TODO change
4+
5+
if ($cookie == 'YOUR_COOKIE_HERE') {
6+
echo 'Please add your _U cookie to multi.php (line 3)' . PHP_EOL;
7+
exit(1);
8+
}
9+
10+
require __DIR__ . '/../vendor/autoload.php';
11+
12+
$ai = new \MaximeRenou\BingAI\BingAI($cookie);
13+
14+
$conversation = $ai->createChatConversation()
15+
->withTone(\MaximeRenou\BingAI\Chat\Tone::Creative);
16+
17+
\MaximeRenou\BingAI\Tools::$debug = false; // Set true for verbose
18+
19+
echo 'Type "q" to quit' . PHP_EOL;
20+
21+
while (true) {
22+
echo PHP_EOL . "> ";
23+
$text = rtrim(fgets(STDIN));
24+
25+
if ($text == 'q')
26+
break;
27+
28+
$prompt = new \MaximeRenou\BingAI\Chat\Prompt($text);
29+
$padding = 0;
30+
31+
list($text, $cards) = $conversation->ask($prompt, function ($text, $cards) use (&$padding) {
32+
// Erase the last line
33+
for ($i = 0; $i < $padding; $i++)
34+
echo chr(8);
35+
36+
$text = trim($text);
37+
38+
// Print partial answer
39+
echo "- $text";
40+
$padding = strlen($text) + 2;
41+
});
42+
43+
// Erase the last line
44+
for ($i = 0; $i < $padding; $i++)
45+
echo chr(8);
46+
47+
// Print final answer
48+
echo "- $text" . PHP_EOL;
49+
50+
// Generative cards
51+
foreach ($cards as $card) {
52+
if ($card->type == \MaximeRenou\BingAI\Chat\MessageType::GenerateQuery && $card->data['contentType'] == 'IMAGE') {
53+
$loader = "Generating: {$card->text}...";
54+
echo $loader;
55+
56+
// Create the image
57+
$creator = $ai->createImages($card->text);
58+
$creator->wait();
59+
60+
// Result
61+
for ($i = 0; $i < strlen($loader); $i++)
62+
echo chr(8);
63+
64+
if ($creator->hasFailed()) {
65+
echo "[Image generation failed]" . PHP_EOL;
66+
}
67+
else {
68+
foreach ($creator->getImages() as $image) {
69+
echo "* $image" . PHP_EOL;
70+
}
71+
72+
$remaining = $creator->getRemainingBoosts();
73+
echo "[$remaining remaining boosts]" . PHP_EOL;
74+
}
75+
}
76+
}
77+
78+
if ($conversation->ended()) {
79+
echo "[Conversation ended]" . PHP_EOL;
80+
break;
81+
}
82+
}
83+
84+
exit(0);

src/Chat/Conversation.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Conversation
2727
protected $current_messages;
2828
protected $user_messages_count;
2929
protected $max_messages_count;
30+
protected $kicked = false;
3031

3132
public function __construct($cookie, $identifiers = null, $invocations = 0)
3233
{
@@ -76,6 +77,16 @@ public function getRemainingMessages()
7677
return $this->max_messages_count - $this->user_messages_count;
7778
}
7879

80+
public function kicked()
81+
{
82+
return $this->kicked;
83+
}
84+
85+
public function ended()
86+
{
87+
return $this->kicked || $this->getRemainingMessages() <= 0;
88+
}
89+
7990
public function createIdentifiers($cookie)
8091
{
8192
$data = Tools::request("https://www.bing.com/turing/conversation/create", [
@@ -241,7 +252,11 @@ public function handlePacket($raw, $connection, $message, $callback)
241252
'InternalLoaderMessage',
242253
'RenderCardRequest',
243254
'AdsQuery',
244-
'SemanticSerp'
255+
'SemanticSerp',
256+
'Disengaged',
257+
'ActionRequest',
258+
'GenerateContentQuery',
259+
'SearchQuery',
245260
],
246261
'sliceIds' => [],
247262
'traceId' => $trace_id,
@@ -358,6 +373,8 @@ public function handleObject($object, $callback = null)
358373
foreach ($this->current_messages as $message) {
359374
if ($message->type == MessageType::Answer)
360375
$text_parts[] = $message->toText();
376+
elseif ($message->type == MessageType::Disengaged)
377+
$this->kicked = true;
361378
}
362379

363380
$this->current_text = trim(implode('. ', array_filter($text_parts)));

src/Chat/Message.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,35 @@ public static function fromData($data)
1919

2020
switch ($data['messageType'] ?? '') {
2121
case 'InternalSearchQuery':
22-
$message->type = MessageType::SearchQuery;
22+
$message->type = MessageType::InternalSearchQuery;
2323
break;
2424
case 'InternalSearchResult':
2525
$message->type = MessageType::SearchResult;
2626
break;
2727
case 'InternalLoaderMessage':
2828
$message->type = MessageType::Loader;
2929
break;
30+
case 'SemanticSerp':
31+
$message->type = MessageType::SemanticSerp;
32+
break;
33+
case 'Disengaged':
34+
$message->type = MessageType::Disengaged;
35+
break;
36+
case 'AdsQuery':
37+
$message->type = MessageType::AdsQuery;
38+
break;
39+
case 'ActionRequest':
40+
$message->type = MessageType::ActionRequest;
41+
break;
3042
case 'RenderCardRequest':
3143
$message->type = MessageType::RenderRequest;
3244
break;
45+
case 'SearchQuery':
46+
$message->type = MessageType::SearchQuery;
47+
break;
48+
case 'GenerateContentQuery':
49+
$message->type = MessageType::GenerateQuery;
50+
break;
3351
default:
3452
$message->type = $data['author'] == 'user' ? MessageType::Prompt : MessageType::Answer;
3553
}
@@ -76,4 +94,4 @@ public function jsonSerialize(): mixed
7694
{
7795
return $this->toArray();
7896
}
79-
}
97+
}

src/Chat/MessageType.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ class MessageType
66
{
77
const Answer = "answer";
88
const Prompt = "prompt";
9-
const SearchQuery = "search_query";
9+
const InternalSearchQuery = "internal_search_query";
1010
const SearchResult = "search_result";
1111
const Loader = "loader";
1212
const RenderRequest = "render_request";
13-
}
13+
const SemanticSerp = "semantic_serp";
14+
const Disengaged = "disengaged";
15+
const AdsQuery = "ads_query";
16+
const ActionRequest = "action_request";
17+
const SearchQuery = "search_query";
18+
const GenerateQuery = "generate_query";
19+
}

0 commit comments

Comments
 (0)