♊ ✦ Forth Day! ✦ ♊
══════════════════════════
ESP32forth talks to Gemini
══════════════════════════
November 16, 2024
GOAL:
Make an ESP32
talk to Gemini
using Forth!
Background
══════════
• ESP32forth (aka µEforth)
- C implementation of Forth
- Leverages X-Macros
- 4 "big" opcodes + many ~1 line opcodes
- Then bootstraps from Forth code in a string
- Buildable as an Arduino .ino
- Optional modules
• Gemini
- Google's AI Chatbot
- Multimodal Large Language Model
- Gemini API
ESP32 Family
════════════
• Espressif Systems
• Built-in WiFi + Bluetooth + more
• ~512KB memory
• ~4MB flash
• Solid Arduino IDE support
• ESP32 / ESP32-S2 / ESP32-S3
(Tensilica Xtensa LX6/7)
• ESP32-C3 (RISC-V)
@esp32.jpg
@t-dongle-s3.png
HTTPS ✦
ESP32 ──────────────▶ Gemini
JSON
HTTPS
═════
• Hypertext Transfer Protocol Secure
• Wrap TCP connect with Transport Layer Security
• Encrypts communication with a symmetric cipher
(usually AES)
• Key exchange and establishing a chain of trust
with an asymmetric cipher
(usually RSA or Elliptic Curve Diffie-Hellman)
- Resists man-in-the-middle attacks
- Requires Certificate Authority Roots of Trust
HTTPClient
══════════
• HTTP / HTTPS Arduino library
• Seems to check chain of trust
• Handles HTTP headers and streaming connections
• Supports connection reuse
• Added to ESP32forth as an "optional" module
Y(name, action) \
X("name", C_NAME, action) \
YV(vocabulary, name, action) \
XV(vocabulary, "name", C_NAME, action) \
n10 n9 n8 n7 n6 n5 n4 n3 n2 n1 n0 - Access stack as cell_t integer values
c4 c3 c2 c1 c0 - Access stack as char* values
b4 b3 b2 b1 b0 - Access stack as uint8_t* byte values
a4 a3 a2 a1 a0 - Access stack as void* values
PUSH val;
DROP; DROPn(n);
NIP; NIPn(n);
NetworkClientSecure.new ( -- nclient )
NetworkClientSecure.delete ( nclient -- )
NetworkClientSecure.setCACert ( certz nclient -- )
#define OPTIONAL_HTTP_CLIENT_SUPPORT \
XV(HTTPClient, "NetworkClientSecure.new", \
NetworkClientSecure_new, PUSH new NetworkClientSecure()) \
XV(HTTPClient, "NetworkClientSecure.delete", \
NetworkClientSecure_delete, delete ((NetworkClientSecure *) a0); DROP) \
XV(HTTPClient, "NetworkClientSecure.setCACert", \
NetworkClientSecure_setCACert, \
((NetworkClientSecure *) a0)->setCACert(c1); DROPn(2)) \
openssl s_client -showcerts -connect generativelanguage.googleapis.com:443
══════════════════════════════════════════════════════════════════════════
CONNECTED(00000003)
depth=2 C = US, O = Google Trust Services LLC, CN = GTS Root R1
verify return:1
depth=1 C = US, O = Google Trust Services, CN = WR2
verify return:1
depth=0 CN = upload.video.google.com
verify return:1
---
Certificate chain
0 s:CN = upload.video.google.com
i:C = US, O = Google Trust Services, CN = WR2
-----BEGIN CERTIFICATE-----
MIIF4TCCBMmgAwIBAgIRAKbTPFDkoO+rCl1CyvNePccwDQYJKoZIhvcNAQELBQAw
....
zZcc0fBNWHsCRIMISrxV5JWe9CX2xlc1u/RZHlJiwhVwQM5n2fsjIzDKxacZD6vY
OFZdBiD3MNlxnEe9bDKclMV0LLZO
-----END CERTIFICATE-----
1 s:C = US, O = Google Trust Services, CN = WR2
i:C = US, O = Google Trust Services LLC, CN = GTS Root R1
-----BEGIN CERTIFICATE-----
MIIFCzCCAvOgAwIBAgIQf/AFoHxM3tEArZ1mpRB7mDANBgkqhkiG9w0BAQsFADBH
....
/oTxUFqOl2stKnn7QGTq8z29W+GgBLCXSBxC9epaHM0myFH/FJlniXJfHeytWt0=
-----END CERTIFICATE-----
2 s:C = US, O = Google Trust Services LLC, CN = GTS Root R1
i:C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
-----BEGIN CERTIFICATE-----
MIIFYjCCBEqgAwIBAgIQd70NbNs2+RrqIQ/E8FjTDTANBgkqhkiG9w0BAQsFADBX ◀─────────
....
d0lIKO2d1xozclOzgjXPYovJJIultzkMu34qQb9Sz/yilrbCgj8=
-----END CERTIFICATE-----
---
-----BEGIN CERTIFICATE-----
MIIFYjCCBEqgAwIBAgIQd70NbNs2+RrqIQ/E8FjTDTANBgkqhkiG9w0BAQsFADBX
MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE
CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIwMDYx
OTAwMDA0MloXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT
GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFIx
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAthECix7joXebO9y/lD63
ladAPKH9gvl9MgaCcfb2jH/76Nu8ai6Xl6OMS/kr9rH5zoQdsfnFl97vufKj6bwS
iV6nqlKr+CMny6SxnGPb15l+8Ape62im9MZaRw1NEDPjTrETo8gYbEvs/AmQ351k
KSUjB6G00j0uYODP0gmHu81I8E3CwnqIiru6z1kZ1q+PsAewnjHxgsHA3y6mbWwZ
DrXYfiYaRQM9sHmklCitD38m5agI/pboPGiUU+6DOogrFZYJsuB6jC511pzrp1Zk
j5ZPaK49l8KEj8C8QMALXL32h7M1bKwYUH+E4EzNktMg6TO8UpmvMrUpsyUqtEj5
cuHKZPfmghCN6J3Cioj6OGaK/GP5Afl4/Xtcd/p2h/rs37EOeZVXtL0m79YB0esW
CruOC7XFxYpVq9Os6pFLKcwZpDIlTirxZUTQAs6qzkm06p98g7BAe+dDq6dso499
iYH6TKX/1Y7DzkvgtdizjkXPdsDtQCv9Uw+wp9U7DbGKogPeMa3Md+pvez7W35Ei
Eua++tgy/BBjFFFy3l3WFpO9KWgz7zpm7AeKJt8T11dleCfeXkkUAKIAf5qoIbap
sZWwpbkNFhHax2xIPEDgfg1azVY80ZcFuctL7TlLnMQ/0lUTbiSw1nH69MG6zO0b
9f6BQdgAmD06yK56mDcYBZUCAwEAAaOCATgwggE0MA4GA1UdDwEB/wQEAwIBhjAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTkrysmcRorSCeFL1JmLO/wiRNxPjAf
BgNVHSMEGDAWgBRge2YaRQ2XyolQL30EzTSo//z9SzBgBggrBgEFBQcBAQRUMFIw
JQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnBraS5nb29nL2dzcjEwKQYIKwYBBQUH
MAKGHWh0dHA6Ly9wa2kuZ29vZy9nc3IxL2dzcjEuY3J0MDIGA1UdHwQrMCkwJ6Al
oCOGIWh0dHA6Ly9jcmwucGtpLmdvb2cvZ3NyMS9nc3IxLmNybDA7BgNVHSAENDAy
MAgGBmeBDAECATAIBgZngQwBAgIwDQYLKwYBBAHWeQIFAwIwDQYLKwYBBAHWeQIF
AwMwDQYJKoZIhvcNAQELBQADggEBADSkHrEoo9C0dhemMXoh6dFSPsjbdBZBiLg9
NR3t5P+T4Vxfq7vqfM/b5A3Ri1fyJm9bvhdGaJQ3b2t6yMAYN/olUazsaL+yyEn9
WprKASOshIArAoyZl+tJaox118fessmXn1hIVw41oeQa1v1vg4Fv74zPl6/AhSrw
9U5pCZEt4Wi4wStz6dTZ/CLANx8LZh1J7QJVj2fhMtfTJr9w4z30Z209fOU0iOMy
+qduBmpvvYuR7hZL6Dupszfnw0Skfths18dG9ZKb59UhvmaSGZRVbNQpsg3BZlvi
d0lIKO2d1xozclOzgjXPYovJJIultzkMu34qQb9Sz/yilrbCgj8=
-----END CERTIFICATE-----
HTTPClient.new ( -- hclient )
HTTPClient.delete ( hclient -- )
HTTPClient.begin ( url hclient -- err )
HTTPClient.beginNC ( url nclient hclient -- err )
HTTPClient.end ( hclient -- )
HTTPClient.connected ( hclient -- f )
HTTPClient.setReuse ( f hclient -- )
HTTPClient.setUserAgent ( agent hclient -- )
HTTPClient.setAuthorization ( username password hclient -- )
HTTPClient.setFollowRedirects ( f hclient -- )
HTTPClient.setRedirectLimit ( n hclient -- )
HTTPClient.GET ( hclient -- code )
HTTPClient.POST ( a n hclient -- code )
HTTPClient.sendRequest ( methodz a n hclient -- code )
HTTPClient.getSize ( hclient -- n )
HTTPClient.getString ( a n hclient -- )
HTTPClient.getStreamPtr ( hclient -- stream )
NetworkClient.available ( stream -- n )
NetworkClient.readBytes ( a n stream -- n )
JSON
════
• JavaScript Object Notation
• Simple types:
- Float64 (covers int32 range also)
- String
- Boolean (true, false)
- null
• Complex types:
- Array [items...]
- Object / Dictionary { key: value, ... }
• Good format for protocol messages
- Human readable, reasonably compact
- Self describing
- Schema-less
{
"mykey": [
1,
2,
3,
4,
5
],
"key2": {
"red": "#ff0000",
"green": "#00ff00",
"blue": "#0000ff"
}
}
But in Forth?
═════════════
• Need a way to represent nest structures
- But doesn't need to support cycles
- Can be reference counted!
• Forth lets us build new types + stacks
• How about APL / CoSy style ARRAYS?
Arrays are all you need?
════════════════════════
• Wisdom of APL:
- Offers dense chunks of like typed values
- Fall back to MIXED references to other arrays
• Makes things uniform
• Supports a pattern to promote types as needed
• Dictionaries...
128 constant stack-depth
( Stack for arrays )
create astack stack-depth cells allot
variable ap astack ap !
: apush ( a -- ) cell ap +! ap @ ! ;
: apop ( -- a ) ap @ @ cell negate ap +! ;
: top ( -- a ) ap @ @ ;
: under ( -- a ) ap @ cell - @ ;
( Secondary stack for arrays )
create arstack stack-depth cells allot
variable arp arstack arp !
: >a apop cell arp +! arp @ ! ;
: a> arp @ @ cell negate arp +! apush ;
┌─────────────────┬──────┬─────────────┬───────···
│ Reference Count │ Size │ Type │ Data ···
└─────────────────┴──────┴─────────────┴───────···
^
┌─────────────────┬──────┬─────────────┬─────────────────···
│ Reference Count │ Size │ 0 (MIXED) │ Array0, Array1, ···
└─────────────────┴──────┴─────────────┴─────────────────···
^
┌─────────────────┬──────┬─────────────┬─────────────────···
│ Reference Count │ Size │ 1 (STRING) │ Char0, Char1, ···
└─────────────────┴──────┴─────────────┴─────────────────···
^
┌─────────────────┬──────┬─────────────┬─────────────────···
│ Reference Count │ Size │ 2 (INTEGER) │ Int0, Int1, Int2···
└─────────────────┴──────┴─────────────┴─────────────────···
^
┌─────────────────┬──────┬─────────────┬─────────────────···
│ Reference Count │ Size │ 3 (REAL) │ Float0, Float1 ···
└─────────────────┴──────┴─────────────┴─────────────────···
^
( Array types )
0 constant MIXED
1 constant STRING
2 constant INTEGER
3 constant REAL
create array-sizes cell , 1 , cell , sfloat ,
: >esize ( type -- n ) cells array-sizes + @ ;
( ref n type ^data... )
3 cells constant header-size
: >type ( a -- a ) -1 cells + ;
: >count ( a -- a ) -2 cells + ;
: >ref ( a -- a ) -3 cells + ;
( Size of array data in bytes )
: bytes ( a -- n ) dup >type @ >esize swap >count @ * ;
( To string / array )
: range ( a -- a n ) dup >count @ ;
( Create an uninitialized array )
: array ( n type -- a: a )
2dup >esize * header-size + allocate throw header-size + apush
top >type ! top >count ! 0 top >ref ! ;
( Reference counting for arrays )
: ref ( a -- ) 1 over >ref +! ;
: unref ( a -- )
dup 0= if drop exit then
-1 over >ref +!
dup >ref @ 0< if
dup >type @ MIXED = if
dup dup >count @ 0 ?do
dup @ recurse cell+
loop
drop
then
header-size - free throw exit
then drop ;
( Stack manipulation )
: adrop ( a: a -- ) apop unref ;
: a2drop ( a: a a -- ) adrop adrop ;
: anip ( a: a b -- b ) apop apop unref apush ;
: adup ( a: a -- a a ) top ref apush ;
: aswap ( a: a b -- b a ) apop apop swap apush apush ;
: aover ( a: a b -- a b a ) apop apop ref dup apush swap apush apush ;
: a2dup ( a: a b -- a b a b ) aover aover ;
( Index into the top of the stack )
: a@ ( n a: a -- a: a ) cells top + @ ref adrop apush ;
( Raw array creation words )
: empty ( -- a: a ) 0 MIXED array ;
: box ( a: a -- a ) apop 1 MIXED array top ! ;
: _s ( a n -- a: a ) dup STRING array top swap cmove ;
: _c ( ch -- a: a ) 1 STRING array top c! ;
: _i ( n -- a: a ) 1 INTEGER array top ! ;
: _f ( f: n -- a: a ) 1 REAL array top sf! ;
: _s" postpone s" state @ if postpone _s else _s then ; immediate
: aconstant create apop , does> @ ref apush ;
( Building arrays on the stack. )
: [[ ap @ ;
: ]] ap @ swap - cell/ empty for aft aswap box aswap ,c then next ;
( Convert integer array to floats )
: n>f
top >count @ REAL array
under top range 0 ?do over @ s>f dup sf! sfloat+ >r cell+ r> loop 2drop anip ;
( Force integers to real. )
: binuminal
top >type @ INTEGER = under >type @ REAL = and if n>f then
under >type @ INTEGER = top >type @ REAL = and if apop n>f apush then
;
0 value layer
: lst ( a -- )
layer spaces
dup >type @ case
MIXED of
." [" cr
2 +to layer
dup >count @ 0 ?do
dup @ recurse cell+ cr
loop
drop
-2 +to layer
layer spaces ." ]"
endof
STRING of dup >count @ type endof
INTEGER of dup >count @ 0 ?do dup @ . cell+ loop drop endof
REAL of dup >count @ 0 ?do dup sf@ f. sfloat+ loop drop endof
endcase
;
: a. ( a -- ) top lst adrop ;
: catenate ( a: a a -- a ) ( catenate )
binuminal
top >type @ under >type @ = if
under >count @ top >count @ + top >type @ array apop >r
under r@ under bytes cmove
top r@ under bytes + top bytes cmove
under under bytes 0 fill
top top bytes 0 fill
r> apush anip anip
exit
then
top >type @ MIXED = if apop box apush recurse exit then
under >type @ MIXED = if box recurse exit then
apop apop 2 MIXED array top cell+ ! top !
;
: ,c catenate ;
That's enough for JSON!
Parsing JSON
════════════
• Recursive descent parser
- Code mirrors grammar
• Single character lookahead
defer getchar
-1 value token
: skip getchar to token ;
variable insource
variable inlength
: in ( a n -- ) inlength ! insource ! skip ;
: ingetchar ( -- n )
inlength @ 0= if -1 exit then
insource @ c@ 1 insource +! -1 inlength +! ;
' ingetchar is getchar
: expect ( a n -- )
for aft dup c@ token = assert 1+ skip then next drop ;
: sliteral ( a n -- ) postpone $@ dup , zplace ;
: e: bl parse sliteral postpone expect ; immediate
╭──string──╮
│ │
├──number──┤
│ │
value ├──object──┤
────────whitespace───┤ ├───whitespace─────▶
├──array───┤
│ │
├──'true'──┤
│ │
├─'false'──┤
│ │
╰──'null'──╯
defer <value>
....
:noname
<whitespace>
token case
[char] " of <string> endof
[char] { of <object> endof
[char] [ of <array> endof
[char] t of e: true true endof
[char] f of e: false false endof
[char] n of e: null null endof
<number>
endcase
<whitespace>
; is <value>
s" null" _s aconstant null
s" true" _s aconstant true
s" false" _s aconstant false
╭───────────────────╮
│ │
├─────linefeed──────┤
│ │
├──carriage return──┤
│ │
├────────tab────────┤
whitespace │ │
────────────╭───┤ ├────────▶
│ │ ╭╯
│ ╰───────space──────┤
│ ╰╮
╰───────────────────────╯
: space? ( ch -- f )
dup 8 =
over 10 = or
over 13 = or
swap 32 = or ;
: <whitespace> begin token space? while skip repeat ;
array ╭────whitespace────╮
────────[───┤ ├───]─────▶
│ ╭╯
╭──,────╰──────value──────┤
│ ╰╮
╰──────────────────────────╯
: <array>
e: [ <whitespace>
0 MIXED array
begin
token [char] ] = if skip exit then
<value> box catenate <whitespace>
token [char] ] = if skip exit then
e: , <whitespace>
again
;
object ╭───────────────────whitespace─────────────────╮
────────{───┤ ├───}─────▶
│ ╭╯
╭──,────╰───whitespace──string──whitespace──:──value──┤
│ ╰╮
╰──────────────────────────────────────────────────────╯
: <object>
e: { <whitespace>
DICT box
begin
token [char] } = if skip exit then
<string> box <whitespace> e: : <whitespace> <value> box
catenate box catenate
token [char] } = if skip exit then
e: , <whitespace>
again
e: }
;
╭──────────────────╮
│ │
├────not \ or "────┤
string │ │
────────"───╭───┤ ├───"─────▶
│ │ ╭╯
│ ╰─────escaped─────┤
│ ╰╮
╰──────────────────────╯
: <string>
e: " s" " _s
begin token [char] " <> while
token [char] \ = if
<escaped>
else
token _c catenate
skip
then
repeat
e: "
;
╭───"───╮
│ │
├───\───┤
│ │
escaped ├───/───┤
────────────\──┤ ├──────────────────────────▶
├───b───┤backspace
│ │
├───f───┤formfeed
│ │
├───n───┤nl
│ │
├───r───┤cr
│ │
├───t───┤tab
│ ╰───────────────╮
│ │
╰───u────4_hex_digits───╯
: <escaped>
e: \
token skip case
[char] " of [char] " _c catenate endof
[char] \ of [char] \ _c catenate endof
[char] / of [char] / _c catenate endof
[char] b of 8 _c catenate endof
[char] f of 12 _c catenate endof
[char] n of nl _c catenate endof
[char] r of 13 _c catenate endof
[char] t of 9 _c catenate endof
[char] u of 255 _c catenate skip skip skip skip endof
-1 throw
endcase
;
number
══════
[-]888 -- mantissa no-leading zeros
[.888] -- fractional part
[E+888]
[E-888] -- exponent
[E888]
: digit? ( -- f ) token [char] 0 >= token [char] 9 <= and ;
: <digit> token [char] 0 - skip ;
: <integer> digit? assert <digit>
begin digit? while 10 * <digit> + repeat ;
: <fraction> digit? assert 10 * <digit> + >r 1- r>
begin digit? while 10 * <digit> + >r 1- r> repeat ;
: <number>
token [char] - = if skip -1 else 1 then
token [char] 0 = if skip 0 else <integer> then 0 swap
token [char] . = if skip <fraction> then
swap >r * r>
token [char] e = token [char] E = or if
skip
token [char] - = if
skip -1
else
token [char] + = if skip then 1
then
<integer> * +
then
dup if 10e s>f f** s>f f* _f else drop _i then
;
: json> ( a -- a ) top range in <value> anip ;
What about printing JSON?
: butlast? ( n -- f ) top >count @ 1- <> ;
: escaped ( a n -- a: a )
_s" "
0 ?do
dup i + c@
case
[char] " of _s" \" ,c [char] " _c ,c endof
[char] / of _s" \/" ,c endof
[char] \ of _s" \\" ,c endof
8 of _s" \b" ,c endof
12 of _s" \f" ,c endof
nl of _s" \n" ,c endof
13 of _s" \r" ,c endof
9 of _s" \t" ,c endof
dup _c ,c
endcase
loop
drop
;
: >json ( a: a -- a )
top >type @ case
MIXED of
top >count @ 1 > if top @ DICT top adrop = else 0 then if
_s" {" >a top >count @ 1 ?do
adup i a@ 0 a@ recurse _s" :" ,c a> aswap ,c >a
adup i a@ 1 a@ recurse a> aswap ,c >a
i butlast? if a> _s" ," ,c >a then
loop a> _s" }" ,c
else
_s" [" >a top >count @ 0 ?do
adup i a@ recurse a> aswap ,c >a
i butlast? if a> _s" ," ,c >a then
loop a> _s" ]" ,c
then
endof
STRING of
top null top adrop = if exit then
top true top adrop = if exit then
top false top adrop = if exit then
[char] " _c >a top range a> escaped ,c [char] " _c ,c
endof
INTEGER of
_s" " >a
top >count @ 0 ?do
top i cells + @ <# #s #> a> _s" " ,c _s ,c >a
loop a> endof
REAL of
_s" " >a top >count @ 0 ?do
top i sfloats + sf@ <# #fs #> a> _s" " ,c _s ,c >a
loop a> endof
endcase
anip
;
Dictionaries
════════════
• Need to represent key, value pairs
• Need to be able to tell them apart
- Use address equality!
• Ideally, fast, but meh
[
"DICTIONARY"
[key1, value1]
[key2, value2]
...
]
s" DICTIONARY" _s aconstant DICT
: {{ [[ DICT ;
: }} ]] ;
( Dictionary lookup )
: as= ( a: a a -- f )
top >type @ STRING <> if a2drop 0 exit then
under >type @ STRING <> if a2drop 0 exit then
top range under range str= a2drop
;
: dict@ ( a: a key -- value )
aswap
top >count @ 1 ?do
a2dup i a@ 0 a@ as= if i a@ 1 a@ anip unloop exit then
loop
a2drop
null
;
Gemini API
══════════
• Transport
- REST API (Representational State Transfer)
- Send JSON, Get JSON
• Features
- Fine-tuning for a task
- Structured Output
- Grounding with Google Search
- Function calling
- Document, Audio, and Image input
- Image output
- System Intructions (who to reply as)
- Long context windows (>99.7% recall up to 1M tokens)
POST to:
https://generativelanguage.googleapis.com/v1beta/models
/gemini-1.5-flash:generateContent?key=GOOGLE_API_KEY
This:
{
"contents": [
{
"parts":[
{"text": "Write a story about a magic backpack."}
]
}
]
}
Result
══════
{
"candidates": [
{
"content": {
"parts": [
{
────────▶ "text": "Lila had always been a dreamer. ...."
}
],
"role": "model"
},
"finishReason": "STOP",
"index": 0,
"safetyRatings": [
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_HARASSMENT",
"probability": "NEGLIGIBLE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"probability": "NEGLIGIBLE"
}
]
}
],
"usageMetadata": {
"promptTokenCount": 8,
"candidatesTokenCount": 570,
"totalTokenCount": 578
},
"modelVersion": "gemini-1.5-flash-001"
}
Parts
═════
{
"contents": [
{"role":"user", "parts":[{
"text": "Hello"
}]},
{"role": "model", "parts":[{
"text": "Great to meet you. What would you like to know?"
}]},
{"role":"user", "parts":[{
"text": "I have two dogs in my house. How many paws are in my house?"}]},
]
}
Parameters
══════════
{
...
"safetySettings": [
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_ONLY_HIGH"
}
],
"generationConfig": {
"stopSequences": [
"Title"
],
"temperature": 1.0,
"maxOutputTokens": 800,
"topP": 0.8,
"topK": 10
}
}
: slurp ( a: filename -- a: content )
top range r/o open-file throw >r adrop
r@ file-size throw STRING array
top range r@ read-file throw drop
r> close-file throw
;
: 0,c ( a -- a\0 ) 0 _c ,c ;
_s" /spiffs/gemini_cert" slurp 0,c aconstant cacert
_s" https://generativelanguage.googleapis.com
/v1beta/models/gemini-1.5-flash:generateContent?key="
_s" /spiffs/gemini_key" slurp json> _s" key" dict@ ,c 0,c aconstant url
also HTTPClient
NetworkClientSecure.new constant nclient
cacert top adrop nclient NetworkClientSecure.setCACert
{
"key": "My Big Secret!"
}
@console.png
: askit ( a: a -- a )
>a
{{
[[ _s" contents" [[
{{
[[ _s" parts" [[
{{
[[ _s" text" a> ]]
}}
]] ]]
}}
]] ]]
}}
;
: reply-text ( a: a -- a )
json> _s" candidates" dict@ 0 a@ _s" content" dict@ _s" parts" dict@ 0 a@ _s" text" dict@
;
: snipped ( a -- a ) top 3 + top >count @ 3 - _s anip ;
: ask
nl parse _s askit >json doquery
snipped reply-text a. cr ;
: askjson ( a -- a )
>a {{
[[ _s" contents" [[
{{
[[ _s" parts" [[
{{
[[ _s" text" a> ]]
}}
]] ]]
}}
]] ]]
[[ _s" generationConfig" {{
[[ _s" response_mime_type" _s" application/json" ]]
}} ]]
}}
;
: asklist ( a -- a )
_s" using this JSON schema: list[str]" ,c askjson >json doquery
snipped reply-text json> ;
DEMO &
QUESTIONS❓
🙏
Thank you!