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

Commit ca23404

Browse files
committed
feat: image analysis
1 parent 52972d8 commit ca23404

File tree

6 files changed

+145
-19
lines changed

6 files changed

+145
-19
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
__New feature: send images in chat 🔥__
2+
13
![Bing + PHP](logo.png)
24

35
# Bing AI client
@@ -80,6 +82,23 @@ list($text, $cards) = $conversation->ask(new Prompt("Hello World"));
8082
> `$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
8183
about its format.
8284

85+
<details>
86+
<summary>🔥 Image analysis</summary>
87+
88+
You may attach an image to your message:
89+
90+
```php
91+
$prompt = new Prompt("How cute is this animal?");
92+
$prompt->withImage('/path/to/panda.png');
93+
//or: $prompt->withImage($raw_image_data, true);
94+
95+
$conversation->ask($prompt, ...);
96+
```
97+
98+
> **Try it!** Type _`$image`_ at the end of your message with `examples/chat.php`.
99+
100+
</details>
101+
83102
<details>
84103
<summary>Real-time / progressive answer</summary>
85104

examples/chat.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,16 @@
2525
if ($text == 'q')
2626
break;
2727

28-
$prompt = new \MaximeRenou\BingAI\Chat\Prompt($text);
28+
if (strpos($text, '$image') !== false) {
29+
$text = str_replace('$image', '', $text);
30+
$prompt = new \MaximeRenou\BingAI\Chat\Prompt($text);
31+
echo PHP_EOL . "Image path: ";
32+
$image = rtrim(fgets(STDIN));
33+
$prompt->withImage($image);
34+
} else {
35+
$prompt = new \MaximeRenou\BingAI\Chat\Prompt($text);
36+
}
37+
2938
$padding = 0;
3039

3140
list($text, $cards) = $conversation->ask($prompt, function ($text, $cards) use (&$padding) {

src/Chat/Conversation.php

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ class Conversation
88
{
99
const END_CHAR = '';
1010

11+
protected $cookie;
12+
1113
// Conversation IDs
1214
public $id;
1315
public $client_id;
@@ -31,8 +33,10 @@ class Conversation
3133

3234
public function __construct($cookie, $identifiers = null, $invocations = 0)
3335
{
36+
$this->cookie = $cookie;
37+
3438
if (! is_array($identifiers))
35-
$identifiers = $this->createIdentifiers($cookie);
39+
$identifiers = $this->createIdentifiers();
3640

3741
$this->id = $identifiers['conversationId'];
3842
$this->client_id = $identifiers['clientId'];
@@ -87,10 +91,10 @@ public function ended()
8791
return $this->kicked || $this->getRemainingMessages() <= 0;
8892
}
8993

90-
public function createIdentifiers($cookie)
94+
public function createIdentifiers()
9195
{
9296
$data = Tools::request("https://www.bing.com/turing/conversation/create", [
93-
'cookie: _U=' . $cookie,
97+
'cookie: _U=' . $this->cookie,
9498
'method: GET',
9599
'accept: application/json',
96100
"accept-language: {$this->region},{$this->locale};q=0.9",
@@ -106,6 +110,50 @@ public function createIdentifiers($cookie)
106110
return $data;
107111
}
108112

113+
public function uploadImage($image_data)
114+
{
115+
$image_encoded = base64_encode($image_data);
116+
117+
$form = [
118+
'knowledgeRequest' => json_encode([
119+
"imageInfo" => (object) [],
120+
"knowledgeRequest" => [
121+
"invokedSkills" => ["ImageById"],
122+
"subscriptionId" => "Bing.Chat.Multimodal",
123+
"invokedSkillsRequestData" => [
124+
"enableFaceBlur" => true
125+
],
126+
"convoData" => [
127+
"convoid" => '', // $this->id ?
128+
"convotone" => 'Precise' // $this->tone ?
129+
]
130+
]
131+
]),
132+
'imageBase64' => $image_encoded
133+
];
134+
135+
Tools::debug("Image upload request: " . print_r($form, true));
136+
137+
$data = Tools::request("https://www.bing.com/images/kblob", [
138+
'cookie: _U=' . $this->cookie,
139+
'method: POST',
140+
'Content-Type: multipart/form-data',
141+
'referer: https://www.bing.com',
142+
], $form);
143+
144+
Tools::debug("Image upload response: $data");
145+
146+
$data = json_decode($data, true);
147+
148+
if (! is_array($data) || empty($data['blobId']) || empty($data['processedBlobId']))
149+
throw new \Exception("Failed to upload image");
150+
151+
return [
152+
'originalImageUrl' => 'https://www.bing.com/images/blob?bcid=' . $data['blobId'],
153+
'imageUrl' => 'https://www.bing.com/images/blob?bcid=' . $data['processedBlobId'],
154+
];
155+
}
156+
109157
public function ask(Prompt $message, $callback = null)
110158
{
111159
$this->invocations++;
@@ -211,9 +259,17 @@ public function handlePacket($raw, $connection, $message, $callback)
211259
'weasgv2',
212260
'dv3sugg',
213261
'inputlanguage',
214-
'rediscluster'
262+
'rediscluster',
263+
264+
// V3 options
265+
"iyxapbing",
266+
"iycapbing",
267+
"enpcktrk",
268+
"logosv1",
269+
"iyolojb",
215270
];
216271

272+
217273
if ($this->tone === Tone::Creative) {
218274
$options = array_merge($options, [
219275
'h3imaginative',
@@ -226,24 +282,51 @@ public function handlePacket($raw, $connection, $message, $callback)
226282
$options = array_merge($options, [
227283
'h3precise',
228284
'clgalileo',
229-
'gencontentv5'
285+
'gencontentv5',
286+
287+
// V3 options
288+
'gencontentv3',
230289
]);
231290
}
232291
else {
233292
$options = array_merge($options, [
234293
'galileo',
235294
'visualcreative',
295+
296+
// V3 options
297+
'harmonyv3',
298+
'saharagenconv5',
236299
]);
237300
}
238301

239302
if (! $message->cache) {
240303
$options[] = "nocache";
241304
}
242305

306+
$message_data = [
307+
'locale' => $message->locale ?? $this->locale,
308+
'market' => $message->market ?? $this->market,
309+
'region' => $message->region ?? $this->region,
310+
'location' => $location,
311+
'locationHints' => $locationHints,
312+
'timestamp' => date('Y-m-d') . 'T' . date('H:i:sP'),
313+
'author' => 'user',
314+
'inputMethod' => 'Keyboard',
315+
'text' => $message->text,
316+
'messageType' => 'Chat',
317+
];
318+
319+
if (! empty($message->image)) {
320+
$message_data = array_merge($message_data, $this->uploadImage($message->image));
321+
$message->image = null;
322+
}
323+
243324
$params = [
244325
'arguments' => [
245326
[
246327
'source' => 'cib',
328+
'verbosity' => 'verbose',
329+
'scenario' => 'SERP',
247330
'optionsSets' => $options,
248331
'allowedMessageTypes' => [
249332
'Chat',
@@ -257,25 +340,19 @@ public function handlePacket($raw, $connection, $message, $callback)
257340
'ActionRequest',
258341
'GenerateContentQuery',
259342
'SearchQuery',
343+
344+
// V3
345+
'Context',
346+
'Progress',
260347
],
261348
'sliceIds' => [],
262349
'traceId' => $trace_id,
263350
'isStartOfSession' => $this->invocations == 0,
264-
'message' => [
265-
'locale' => $message->locale ?? $this->locale,
266-
'market' => $message->market ?? $this->market,
267-
'region' => $message->region ?? $this->region,
268-
'location' => $location,
269-
'locationHints' => $locationHints,
270-
'timestamp' => date('Y-m-d') . 'T' . date('H:i:sP'),
271-
'author' => 'user',
272-
'inputMethod' => 'Keyboard',
273-
'text' => $message->text,
274-
'messageType' => 'Chat',
275-
],
351+
'message' => $message_data,
276352
'conversationSignature' => $this->signature,
277353
'participant' => ['id' => $this->client_id],
278-
'conversationId' => $this->id
354+
'conversationId' => $this->id,
355+
'spokenTextMode' => 'None',
279356
]
280357
],
281358
'invocationId' => "{$this->invocations}",

src/Chat/Message.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ public static function fromData($data)
4848
case 'GenerateContentQuery':
4949
$message->type = MessageType::GenerateQuery;
5050
break;
51+
break;
52+
case 'Context':
53+
$message->type = MessageType::Context;
54+
break;
55+
break;
56+
case 'Progress':
57+
$message->type = MessageType::Progress;
58+
break;
5159
default:
5260
$message->type = $data['author'] == 'user' ? MessageType::Prompt : MessageType::Answer;
5361
}

src/Chat/MessageType.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ class MessageType
1616
const ActionRequest = "action_request";
1717
const SearchQuery = "search_query";
1818
const GenerateQuery = "generate_query";
19+
const Context = "context";
20+
const Progress = "progress";
1921
}

src/Chat/Prompt.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class Prompt
99
public $market;
1010
public $region;
1111
public $text;
12+
public $image;
1213

1314
public function __construct($text)
1415
{
@@ -28,4 +29,14 @@ public function withoutCache()
2829
$this->cache = false;
2930
return $this;
3031
}
32+
33+
public function withImage($image, $is_rawdata = false)
34+
{
35+
if (! $is_rawdata) {
36+
$image = file_get_contents($image);
37+
}
38+
39+
$this->image = $image;
40+
return $this;
41+
}
3142
}

0 commit comments

Comments
 (0)