darsoarafa commited on
Commit
87de344
·
verified ·
1 Parent(s): 6b6c410

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +632 -632
app.py CHANGED
@@ -1,633 +1,633 @@
1
- from __future__ import annotations as _annotations
2
- import os
3
- import asyncio
4
- import json
5
- import sqlite3
6
- import datetime
7
- import fastapi
8
- import logfire
9
- import time
10
- from collections.abc import AsyncIterator
11
- from concurrent.futures.thread import ThreadPoolExecutor
12
- from contextlib import asynccontextmanager
13
- from dataclasses import dataclass
14
- from datetime import datetime, timezone, date
15
- from functools import partial
16
- from pathlib import Path
17
- from typing import Annotated, Any, Callable, Literal, TypeVar
18
-
19
- from pydantic import BaseModel, Field, ValidationError, model_validator
20
- from typing import List, Optional, Dict
21
-
22
- from fastapi import Depends, Request
23
- from fastapi.responses import FileResponse, Response, StreamingResponse
24
- from typing_extensions import LiteralString, ParamSpec, TypedDict
25
-
26
- from pydantic_ai import Agent, ModelRetry, RunContext
27
-
28
- from pydantic_ai.exceptions import UnexpectedModelBehavior
29
- from pydantic_ai.messages import (
30
- ModelMessage,
31
- ModelMessagesTypeAdapter,
32
- ModelRequest,
33
- ModelResponse,
34
- TextPart,
35
- UserPromptPart,
36
- )
37
- from pydantic_ai.models.openai import OpenAIModel
38
- #from pydantic_ai import OpenAIEmbeddings
39
-
40
- #from sentence_transformers import SentenceTransformer
41
- #embedding_model = SentenceTransformer("all-mpnet-base-v2")
42
-
43
- class EmbeddingRequest(BaseModel):
44
- text: str
45
-
46
- class EmbeddingResponse(BaseModel):
47
- embeddings: List[float]
48
-
49
- abc = """
50
- Anda adalah pelayan yang ramah, mampu memberikan info tentang Toko Teh "Arafatea", yaitu toko yang menjual 4 Kategori Produk yaitu (1) Teh Premium, (2) Teh Herbal, (3) Camilan/Snack dan (4) Buah Kering. Untuk Kategori Teh Premium ada 5 produk yaitu Teh Hitam, Teh Hijau, Teh Putih, Teh Genmaicha dan Matcha. Dalam kategori Teh Herbal ada 5 produk yaitu Pandan Tea, Mint Tea, Orange Tea, Teh Buah, dan Teh Rempah. Untuk kategori "Snack" meliputi 4 produk yaitu Greentea-Chocobar, Coffee-Chocobar, Rice-Cracker-Greentea, Rice-Cracker-Coffee; Untuk kategori "Buah Kering" meliputi 8 nama produk meliputi Buah Kering Mangga, Buah Kering Nanas, Buah Kering Lemon, dan Buah Kering Strawberry; Jus Mangga Kering, Jus Nanas Kering, Jus Buah Naga Kering, dan Jus Stawberry Kering. Tidak ada produk selain produk yang masuk 4 kategori tersebut. Alamat website www.arafatea.com, atau www.shopee.co.id/arafatea nomor telepon toko Arafatea adalah 08170218404
51
- """
52
-
53
-
54
-
55
-
56
- #await onFetchResponse(response)
57
- #get_embeddings({"text":abc})
58
-
59
-
60
- model = OpenAIModel(
61
- 'gemma-2-2b-it', #'llama-3.2-1b-instruct',
62
- base_url='http://localhost:1234/v1',
63
- api_key='your-local-api-key',
64
- )
65
- model2 = OpenAIModel(
66
- 'gemma-2-2b-it', #'llama-3.2-1b-instruct',
67
- base_url='http://localhost:52199/api/v1/openai',
68
- api_key='D6K06DS-P474SK6-P2ACYYC-K5516S1',
69
- )
70
- model1 = OpenAIModel(
71
- 'gemma-2-2b-it', #'llama-3.2-1b-instruct',
72
- base_url='http://localhost:52199/api/v1',
73
- api_key='D6K06DS-P474SK6-P2ACYYC-K5516S1',
74
- )
75
-
76
- # Model Pydantic untuk Sales Order
77
- class product1(BaseModel):
78
- ds_productCode: Optional[str] = "" #str = Field(..., min_length=1, max_length=20)
79
- ds_productName: str = Field(..., min_length=1, max_length=100)
80
- ds_quantity: int = Field(..., ge=1)
81
- ds_unitPrice: float = Field(..., ge=0)
82
- ds_itemAmt: Optional[float] = 0
83
-
84
- # 'if-token-present' means nothing will be sent (and the example will work) if you don't have logfire configured
85
- logfire.configure(send_to_logfire='if-token-present')
86
-
87
- class product(BaseModel):
88
- ds_productCode: Optional[str] = "" #str = Field(..., min_length=1, max_length=20)
89
- ds_productName: Optional[str] = ""
90
- ds_quantity: Optional[int] = 0
91
- ds_unitPrice: Optional[float] = 0
92
- ds_itemAmt: Optional[float] = 0
93
-
94
- class SalesOrder(BaseModel):
95
- ds_salesOrderId: Optional[str] = ""
96
- ds_salesDate: Optional[str] = ""
97
- ds_customerName: str = Field(..., min_length=1, max_length=100)
98
- ds_customerAddress: Optional[str] = ""
99
- ds_items: List[product] = Field(..., min_items=1)
100
- ds_shippingCost:Optional[float] = 0
101
- ds_shippingAddress: Optional[str] = ""
102
- ds_totalAmount:Optional[float] = 0
103
- #ds_paymentTerms: Optional[str] = ""
104
- os.environ['GEMINI_API_KEY'] = 'AIzaSyAsVIHsPIIfDBTb2K6VNdNlMt05t8x3mtE'
105
- #agent= Agent('gemini-1.5-flash', result_type=SalesOrder)
106
-
107
- # # Create a system prompt to guide the model
108
- SYSTEM_PROMPT = """
109
- Anda adalah pelayan yang ramah, mampu memberikan info tentang Toko Teh "Arafatea", yaitu toko yang menjual 4 Kategori Produk yaitu (1) Teh Premium, (2) Teh Herbal, (3) Camilan/Snack dan (4) Buah Kering. Untuk Kategori Teh Premium ada 5 produk yaitu P11-Teh Hitam, P12-Teh Hijau, P13-Teh Putih, P14-Teh Genmaicha dan P15-Matcha (Serbuk Teh Hijau). Dalam kategori Teh Herbal ada 5 produk yaitu P21-Pandan Tea, P22-Mint Tea, P23-Orange Tea, P24-Teh Buah, dan P25-Teh Rempah. Untuk kategori "Snack" meliputi 4 produk yaitu P31-Greentea-Chocobar, P32-Coffee-Chocobar, P33-Rice-Cracker-Greentea, P34-Rice-Cracker-Coffee; Untuk kategori "Buah Kering" meliputi 8 nama produk meliputi P41 Buah Mangga Kering, P42-Buah Nanas Kering, P43-Buah Lemon Kering, dan P44-Buah Strawberry Kering; P45-Jus Mangga Kering, P46-Jus Nanas Kering, P47-Jus Buah Naga Kering, dan P48-Jus Stawberry Kering. Tidak ada produk selain produk yang masuk 4 kategori tersebut. Alamat website www.arafatea.com, atau www.shopee.co.id/arafatea nomor telepon toko Arafatea adalah 08170218404.
110
-
111
- Berikut ini adalah informasi tentang masing-masing produk.
112
-
113
- P11-Teh Hitam, adalah teh hitam kualitas terbaik, ditanam di dataran tinggi Bandung dengan hara tanah baik, menghasilkan teh hitam berkualitas.
114
- P12-Teh Hijau, adalah teh hijau kualitas terbaik, ditanam di dataran tinggi Bandung dengan hara tanah baik, menghasilkan teh hijau berkualitas.
115
- P13-Teh Putih, adalah teh putih yang dibuat hanya dari pucuk daun teh yang belum mekar. Dikeringkan dengan mesin dehydrator pada suhu rendah, secara hygienis dan otomatis.
116
- P14-Teh Genmaicha, adalah teh hijau yang dihasilkan dari varietas tanaman Teh Yabugita (Japanese style), terkenal karena rasanya yang enak tanpa rasa sepet/pahit.
117
- P15-Matcha (Serbuk Teh Hijau), adalah daun teh hijau kualitas terbaik yang dijadikan serbuk, biasa dinikmati sebagai macha murni dan matcha latte (dengan tambahan krimer)
118
-
119
- P21 Pandan Tea, adalah sinergi Teh hijau ditambah daun pandan, menghasilkan teh hijau berkualitas dengan aroma harum dari daun pandan.
120
- P22 Mint Tea, adalah sinergi Teh hijau dan daun mint yang menyegarkan.
121
- P23 Orange Tea, adalah sinergi Teh hitam dan irisan buah jeruk, rasanya enak menyegarkan khas teh buah jeruk.
122
- P24 Teh Buah, adalah teh hitam yang bersinergi dengan beberapaa macam buah termasuk lemon dan nanas.
123
- P25 Teh Rempah, adalah teh hijau dengan campuran herbal untuk meningkatkan imunitas, terutama untuk menghadapi cuaca hujan dan konsidi polusi.
124
-
125
- P31-Greentea-Chocobar, adalah coklat berbentuk batang dengan dilapisi teh hijau terbaik, mengandung zat aktif l-theanine dalaam dosis cukup menenangkan.
126
- P32-Coffee-Chocobar, adalah coklat batang dilapisi serbuk kopi, enak menyegarkan.
127
- P33-Rice-Cracker-Greentea, adalah nasi ketan yang dilapisi teh hijau, sungguh enak sekali, sumber energi dan meningkatkan fokus.
128
- P34-Rice-Cracker-Coffee, adalah sumber energi karena mengandung karbohidrat dari nasi ketan, dan kesegaran dari kopi.
129
-
130
- P41 Buah Mangga Kering, berupa irisan tipis buah mangga, dikeringkan dengan mesin dehydrator suhu rendah. Asli buah, tanpa tambahan apapun, 100 persen alami, sumber serat dan vitamin.
131
-
132
- P42 Buah Nanas Kering, berupa irisan tipis buah nanas, dikeringkan dengan mesin dehydrator suhu rendah. Asli buah, tanpa tambahan apapun, 100 persen alami, sumber serat dan vitamin.
133
-
134
- P43 Buah Naga Kering, berupa irisan tipis buah naga, dikeringkan dengan mesin dehydrator suhu rendah. Asli buah, tanpa tambahan apapun, 100 persen alami, sumber serat dan vitamin.
135
-
136
- P44 Buah Strawberry Kering, berupa irisan tipis buah Strawberry, dikeringkan dengan mesin dehydrator suhu rendah. Asli buah, tanpa tambahan apapun, 100 persen alami, sumber serat dan vitamin.
137
-
138
- P45 Jus Mangga Kering, biasa disebut Manggo fruit leather, dibuat dari buah Mangga yang matang di pohon, dibersihkan dan di-jus, kemudian dikeringkan dengan mesin dehydrator suhu rendah. Hasilnya jus buah Mangga yang kering berbentuk lembaran, awet dan 100 persen alami.
139
-
140
- P46 Jus Nanas Kering, biasa disebut Pineapple fruit leather, dibuat dari buah Nanas yang matang di pohon, dibersihkan dan di-jus, kemudian dikeringkan dengan mesin dehydrator suhu rendah. Hasilnya jus buah Nanas yang kering berbentuk lembaran, awet dan 100 persen alami.
141
-
142
- P47 Jus Buah Naga Kering, biasa disebut Dragon fruit leather, dibuat dari buah Naga yang matang di pohon, dibersihkan dan di-jus, kemudian dikeringkan dengan mesin dehydrator suhu rendah. Hasilnya buah Naga kering berbentuk lembaran, awet dan 100 persen alami.
143
-
144
- P48 Jus Strawberry Kering, biasa disebut Strawberry fruit leather, dibuat dari buah strawberry yang matang di pohon, dibersihkan dan di-jus, kemudian dikeringkan dengan mesin dehydrator suhu rendah. Hasilnya strawberry kering berbentuk lembaran, awet dan 100 persen alami.
145
- """
146
- #agent3 = Agent(model=ollama_model, result_type=PetList, retries=3, system_prompt=SYSTEM_PROMPT)
147
- #agent3 = Agent(model=ollama_model, retries=3, system_prompt=SYSTEM_PROMPT)
148
-
149
-
150
- #INI SAJA. SALAH SATU
151
- #agent = Agent('gemini-1.5-flash', system_prompt=SYSTEM_PROMPT) # OK-Gemini
152
- #agent = Agent(model) # OK-Lokal
153
- agent = Agent(model, system_prompt=SYSTEM_PROMPT) # OK-Lokal
154
-
155
- THIS_DIR = Path(__file__).parent
156
-
157
-
158
- @asynccontextmanager
159
- async def lifespan(_app: fastapi.FastAPI):
160
- async with Database.connect() as db:
161
- yield {'db': db}
162
-
163
-
164
- app = fastapi.FastAPI(lifespan=lifespan)
165
- logfire.instrument_fastapi(app)
166
-
167
-
168
- @app.get('/')
169
- async def index() -> FileResponse:
170
- print(THIS_DIR / 'produk.html')
171
- return FileResponse((THIS_DIR / 'produk.html'), media_type='text/html')
172
-
173
-
174
- @app.get('/chat_app.ts')
175
- async def main_ts() -> FileResponse:
176
- """Get the raw typescript code, it's compiled in the browser, forgive me."""
177
- return FileResponse((THIS_DIR / 'chat_app.ts'), media_type='text/plain')
178
-
179
-
180
- async def get_db(request: Request) -> Database:
181
- return request.state.db
182
-
183
- @app.get('/chat/')
184
- async def get_chat(database: Database = Depends(get_db)) -> Response:
185
- msgs = await database.get_messages()
186
- return Response(
187
- b'\n'.join(json.dumps(to_chat_message(m)).encode('utf-8') for m in msgs),
188
- media_type='text/plain',
189
- )
190
-
191
- @app.get('/embed/')
192
- async def get_embeddings(request: EmbeddingRequest):
193
- print(request.text)
194
- embeddings_result = await embedding_model.encode(request.text)
195
- abcde = embeddings_result.tolist()
196
- print(abcde)
197
- return EmbeddingResponse(embeddings=embeddings_result.tolist())
198
-
199
- class ChatMessage(TypedDict):
200
- """Format of messages sent to the browser."""
201
- role: Literal['user', 'model']
202
- timestamp: str
203
- content: str
204
-
205
-
206
- def to_chat_message(m: ModelMessage) -> ChatMessage:
207
- first_part = m.parts[0]
208
- if isinstance(m, ModelRequest):
209
- if isinstance(first_part, UserPromptPart):
210
- return {
211
- 'role': 'user',
212
- 'timestamp': first_part.timestamp.isoformat(),
213
- 'content': first_part.content,
214
- }
215
- elif isinstance(m, ModelResponse):
216
- if isinstance(first_part, TextPart):
217
- return {
218
- 'role': 'model',
219
- 'timestamp': m.timestamp.isoformat(),
220
- 'content': first_part.content,
221
- }
222
- raise UnexpectedModelBehavior(f'Unexpected message type for chat app: {m}')
223
-
224
- def to_ds_message(m: ModelMessage) -> ChatMessage:
225
-
226
- if isinstance(m, ModelRequest):
227
- first_part = m.parts[0]
228
- if isinstance(first_part, UserPromptPart):
229
- return {
230
- 'role': 'user',
231
- 'timestamp': first_part.timestamp.isoformat(),
232
- 'content': first_part.content,
233
- }
234
- elif isinstance(m, ModelResponse):
235
- first_part = m.parts[0]
236
- if isinstance(first_part, TextPart):
237
- return {
238
- 'role': 'model',
239
- 'timestamp': m.timestamp.isoformat(),
240
- 'content': first_part.content,
241
- }
242
- raise UnexpectedModelBehavior(f'Unexpected ds-message type for chat app: {m}')
243
-
244
-
245
-
246
-
247
- @app.post('/chat1/')
248
- async def post_chat1(
249
- prompt: Annotated[str, fastapi.Form()], database: Database = Depends(get_db)
250
- ) -> StreamingResponse:
251
- async def stream_messages():
252
- """Streams new line delimited JSON `Message`s to the client."""
253
- # stream the user prompt so that can be displayed straight away
254
- yield (
255
- json.dumps(
256
- {
257
- 'role': 'user',
258
- 'timestamp': datetime.now(tz=timezone.utc).isoformat(),
259
- 'content': prompt,
260
- }
261
- ).encode('utf-8')
262
- + b'\n'
263
- )
264
- # get the chat history so far to pass as context to the agent
265
- messages = await database.get_messages()
266
- # run the agent with the user prompt and the chat history
267
- async with agent.run_stream(prompt, message_history=messages) as result:
268
- async for text in result.stream(debounce_by=0.01):
269
- # text here is a `str` and the frontend wants
270
- # JSON encoded ModelResponse, so we create one
271
- m = ModelResponse.from_text(content=text, timestamp=result.timestamp())
272
- yield json.dumps(to_chat_message(m)).encode('utf-8') + b'\n'
273
-
274
- # add new messages (e.g. the user prompt and the agent response in this case) to the database
275
- #print("---",result.new_messages_json(),"---")
276
- #print("***",prompt,"***")
277
- #await database.add_messages(result.new_messages_json())
278
- print("** selesai **")
279
- return StreamingResponse(stream_messages(), media_type='text/plain')
280
-
281
- @app.post('/chat/')
282
- async def post_chat(
283
- prompt: Annotated[str, fastapi.Form()], database: Database = Depends(get_db)
284
- ) -> StreamingResponse:
285
- async def stream_messages():
286
- """Streams new line delimited JSON `Message`s to the client."""
287
- # stream the user prompt so that can be displayed straight away
288
- yield (
289
- json.dumps(
290
- {
291
- 'role': 'user',
292
- 'timestamp': datetime.now(tz=timezone.utc).isoformat(),
293
- 'content': prompt,
294
- }
295
- ).encode('utf-8')
296
- + b'\n'
297
- )
298
- # get the chat history so far to pass as context to the agent
299
- messages = await database.get_messages()
300
- # run the agent with the user prompt and the chat history
301
- async with agent.run_stream(prompt, message_history=messages) as result:
302
- async for text in result.stream(debounce_by=0.01):
303
- # text here is a `str` and the frontend wants
304
- # JSON encoded ModelResponse, so we create one
305
- m = ModelResponse.from_text(content=text, timestamp=result.timestamp())
306
- yield json.dumps(to_chat_message(m)).encode('utf-8') + b'\n'
307
-
308
- # add new messages (e.g. the user prompt and the agent response in this case) to the database
309
- #print("---",result.new_messages_json(),"---")
310
- #print("***",prompt,"***")
311
- #await database.add_messages(result.new_messages_json())
312
- async def ds_messages(prompt1):
313
- try:
314
- #prompt2=f"Ekstrak data Sales Order dari teks: {prompt1}. Hari ini adalah tanggal {date.today()}"
315
- #prompt2=f"{prompt1}. Hari ini adalah tanggal {date.today()}"
316
- prompt2=f"{prompt1}"
317
- yield (
318
- json.dumps(
319
- {
320
- 'role': 'user',
321
- 'timestamp': datetime.now(tz=timezone.utc).isoformat(),
322
- 'content': prompt1,
323
- }
324
- ).encode('utf-8')
325
- + b'\n'
326
- )
327
- #messages = await database.get_messages()
328
- async with agent.run_stream(prompt2) as result:
329
- async for text in result.stream(debounce_by=0.1):
330
- m = ModelResponse.from_text(content=text, timestamp=result.timestamp())
331
- yield json.dumps(to_ds_message(m)).encode('utf-8') + b'\n'
332
-
333
- ##print(result.usage())
334
- await database.add_messages(result.new_messages_json())
335
- return
336
- darso = json.loads(result.new_messages_json())
337
- #print(darso)
338
- darso0= darso[0]
339
- #darso0.pop(darso0['parts'][0])
340
- #print("00|------------------")
341
- #print("01|", darso0 )
342
- #print("01a|", darso0['parts'][0] )
343
- #print("01b|", darso0['parts'][1] )
344
- darso0['parts'][0] = darso0['parts'][1]
345
- #print("01?|", darso0 )
346
- #print("01??|", darso )
347
- #print("02|------------------")
348
- darso1= darso[1]
349
- #print("1|", darso1)
350
- darso2= json.loads(json.dumps(darso1))
351
- #print("2|",darso2['parts'][0])
352
- darso3= darso2['parts'][0]
353
- darso4= json.loads(json.dumps(darso3))
354
- #print("4|",darso4['content'])
355
- darso5= darso4['content']
356
- darso5=darso5.split('```', 2)
357
- darso5=darso5[1]
358
- #print("5a|",darso5)
359
- darso5=darso5.replace('json', '')
360
- print("5|",darso5,"|")
361
- try:
362
- darso6= json.loads(darso5) #json
363
- darso7= SalesOrder.model_validate(darso6)
364
- except:
365
- darso6= "ERR"
366
- print("6|",darso6,"|")
367
- if "ds_items" in darso5:
368
- cek_str="ds_items"
369
- else:
370
- cek_str="--"
371
- if darso6=="ERR":
372
- ds_id = time.time()
373
- ds_salesOrderId = "ERR"
374
- ds_salesDate = 'ERR'
375
- ds_customerName="-"
376
- ds_customerAddress="-"
377
-
378
- ds_productName1 = "Produk1 --- "
379
- ds_quantity1 = 1
380
- ds_unitPrice1 = 0
381
- ds_itemAmt1 = 0
382
-
383
- ds_productName2 = "Produk2 --- "
384
- ds_quantity2 = 0
385
- ds_unitPrice2 = 0
386
- ds_itemAmt2 = 0
387
-
388
- ds_productName3 = "Produk3 --- "
389
- ds_quantity3 = 0
390
- ds_unitPrice3 = 0
391
- ds_itemAmt3 = 0
392
- ds_shippingAddress=""
393
- ds_shippingCost=0
394
- ds_totalAmount=0
395
- else:
396
- ds_id = time.time()
397
- ds_salesOrderId = "OK"
398
- ds_salesDate = 'OK'
399
- try:
400
- ds_salesOrderId = darso7.ds_salesOrderId
401
- print("7|ds_salesOrderId")
402
- ds_salesDate = darso7.ds_salesDate
403
- print("7|ds_salesDate")
404
- ds_customerName=f"""{darso7.ds_customerName}"""
405
- print("7|ds_customerName:",ds_customerName)
406
- ds_customerAddress=f"""{darso7.ds_customerAddress}"""
407
- print("7|ds_customerAddress:", len(darso7.ds_items))
408
- ds_productName1 = darso7.ds_items[0].ds_productName
409
- print("7|ds_productName1")
410
- ds_quantity1 = darso7.ds_items[0].ds_quantity
411
- print("7|ds_quantity1")
412
- ds_unitPrice1 = darso7.ds_items[0].ds_unitPrice
413
- print("7|ds_unitPrice1")
414
- ds_itemAmt1 = darso7.ds_items[0].ds_itemAmt
415
- print("7|ds_itemAmt1")
416
- ds_productName2 = "-"
417
- ds_quantity2 = 0
418
- ds_unitPrice2 = 0
419
- ds_itemAmt2 = 0
420
- ds_productName3 = "-"
421
- ds_quantity3 = 0
422
- ds_unitPrice3 = 0
423
- ds_itemAmt3 = 0
424
-
425
- if len(darso7.ds_items)>1:
426
- ds_productName2 = darso7.ds_items[1].ds_productName
427
- ds_quantity2 = darso7.ds_items[1].ds_quantity
428
- ds_unitPrice2 = darso7.ds_items[1].ds_unitPrice
429
- ds_itemAmt2 = darso7.ds_items[1].ds_itemAmt
430
- if len(darso7.ds_items)>2:
431
- ds_productName3 = darso7.ds_items[2].ds_productName
432
- ds_quantity3 = darso7.ds_items[2].ds_quantity
433
- ds_unitPrice3 = darso7.ds_items[2].ds_unitPrice
434
- ds_itemAmt3 = darso7.ds_items[2].ds_itemAmt
435
-
436
- ds_shippingCost=darso7.ds_shippingCost
437
- print("7|ds_shippingCost")
438
- ds_shippingAddress=f"""{darso7.ds_shippingAddress}"""
439
- print("7|ds_shippingAddress")
440
- ds_totalAmount=darso7.ds_totalAmount
441
- print("7|ds_totalAmount")
442
- except:
443
- ds_salesOrderId = "OK2"
444
- ds_salesDate = 'OK2'
445
- ds_customerName="-"
446
- ds_customerAddress="-"
447
-
448
- ds_productName1 = "Produk1"
449
- ds_quantity1 = 0
450
- ds_unitPrice1 = 0
451
- ds_itemAmt1 = 0
452
-
453
- ds_productName2 = "Produk2"
454
- ds_quantity2 = 0
455
- ds_unitPrice2 = 0
456
- ds_itemAmt2 = 0
457
-
458
- ds_productName3 = "Produk3"
459
- ds_quantity3 = 0
460
- ds_unitPrice3 = 0
461
- ds_itemAmt3 = 0
462
- ds_shippingAddress=""
463
- ds_shippingCost=0
464
- ds_totalAmount=0
465
-
466
- formDs = f"""
467
-
468
- <form id="myaiForm{ds_id}" action="javascript:abcde({ds_id});" class="form-container">
469
- <h3>Pesanan</h3>
470
- <table>
471
- <tr>
472
- <td><label for="ds_salesOrderId"><b>SO#</b></label><input type="text" placeholder="" name="ds_salesOrderId" value="{ds_salesOrderId}"></td>
473
- <td><label for="ds_salesDate"><b>Date</b></label><input type="text" placeholder="" name="ds_salesDate" value="{ds_salesDate}"></td>
474
- </tr>
475
- <tr>
476
- <td colspan="2"><label for="ds_customerName"><b>Customer</b></label><input type="text" placeholder="" name="ds_customerName" value="{ds_customerName}"></td>
477
- </tr>
478
- <tr>
479
- <td colspan="2"><label for="ds_customerAddress"><b>Alamat</b></label><input type="text" placeholder="" name="ds_customerAddress" value="{ds_customerAddress}"></td>
480
- </tr>
481
- </table style="width:100%">
482
- <b>Item Barang:</b>
483
- <table>
484
- <tr><th>Prod</th><th style="text-align: center;">Qty</th><th style="text-align: center;">Prc</th><th style="text-align: center;">Rp</th></tr>
485
- <tr>
486
- <td><input type="text" placeholder="-" name="ds_productName1" value="{ds_productName1}"></td>
487
- <td><input type="text" style="text-align: center;" name="ds_quantity1" value={ds_quantity1}></td>
488
- <td><input type="text" style="text-align: center;" name="ds_unitPrice1" value={ds_unitPrice1}></td>
489
- <td><input type="text" style="text-align: center;" name="ds_itemAmt1" value={ds_itemAmt1}></td>
490
- </tr>
491
- <tr>
492
- <td><input type="text" placeholder="-" name="ds_productName2" value="{ds_productName2}"></td>
493
- <td><input type="text" style="text-align: center;" name="ds_quantity2" value={ds_quantity2}></td>
494
- <td><input type="text" style="text-align: center;" name="ds_unitPrice2" value={ds_unitPrice2}></td>
495
- <td><input type="text" style="text-align: center;" name="ds_itemAmt2" value={ds_itemAmt2}></td>
496
- </tr>
497
- <tr>
498
- <td><input type="text" placeholder="-" name="ds_productName3" value="{ds_productName3}"></td>
499
- <td><input type="text" style="text-align: center;" name="ds_quantity3" value={ds_quantity3}></td>
500
- <td><input type="text" style="text-align: center;" name="ds_unitPrice3" value={ds_unitPrice3}></td>
501
- <td><input type="text" style="text-align: center;" name="ds_itemAmt3" value={ds_itemAmt3}></td>
502
- </tr>
503
- </table>
504
- <table>
505
- <tr>
506
- <td style="text-align: center;"><b>Ongkir</b></td>
507
- <td style="text-align: center;"><b>Total</b></td>
508
- </tr>
509
- <tr>
510
- <td><input type="text" style="text-align: center;" placeholder="0" name="ds_shippingCost" value={ds_shippingCost}></td>
511
- <td><input type="text" style="text-align: center;" placeholder="0" name="ds_totalAmount" value={ds_totalAmount}></td>
512
- </tr>
513
- <tr>
514
- <td colspan="2"><label for="ds_shippingAddress"><b></b></label><input type="text" placeholder="" name="ds_shippingAddress" value="{ds_shippingAddress}"></td>
515
- </tr>
516
- </table>
517
- <button type="submit" class="btn">Submit</button>
518
- <button type="button" class="btn cancel" onclick="closeAiForm({ds_id})">Close</button>
519
- </form>
520
- <form id="myaiForm2{ds_id}" class="form-container" style="display:none;">
521
- <button type="button" class="btn umum" onclick="openAiForm({ds_id})">Open Form</button>
522
- </form>
523
- """
524
- m = ModelResponse.from_text(content=formDs, timestamp=result.timestamp())
525
- yield json.dumps(to_ds_message(m)).encode('utf-8') + b'\n'
526
- print("OK:")
527
- #await database.add_messages(result.new_messages_json())
528
- await database.add_messages(json.dumps(darso))
529
- ##print(len(items))
530
- #darso7 = SalesOrder.model_validate(darso6)
531
- #print("[--",darso7.ds_customerName,"--]")
532
- #darso8 = darso7.ds_items[0]
533
- ##, len(darso7.ds_items)
534
- #print("[--",darso8.ds_productName,"--]")
535
- except ValueError as e:
536
- print(e)
537
- if prompt[0] == "@" :
538
- #print("@@@", prompt, "@@@")
539
- nn = len(prompt)
540
- prompt = prompt[1:nn]
541
- print(">>>", prompt, "<<<")
542
- return StreamingResponse(ds_messages(prompt), media_type='text/plain')
543
- elif prompt[0] != "@" :
544
- #print("biasa")
545
- return StreamingResponse(stream_messages(), media_type='text/plain')
546
- print("** selesai **")
547
- return StreamingResponse(stream_messages(), media_type='text/plain')
548
-
549
- P = ParamSpec('P')
550
- R = TypeVar('R')
551
-
552
-
553
- @dataclass
554
- class Database:
555
- """Rudimentary database to store chat messages in SQLite.
556
-
557
- The SQLite standard library package is synchronous, so we
558
- use a thread pool executor to run queries asynchronously.
559
- """
560
-
561
- con: sqlite3.Connection
562
- _loop: asyncio.AbstractEventLoop
563
- _executor: ThreadPoolExecutor
564
-
565
- @classmethod
566
- @asynccontextmanager
567
- async def connect(
568
- cls, file: Path = THIS_DIR / '.chat_messages.sqlite'
569
- ) -> AsyncIterator[Database]:
570
- with logfire.span('connect to DB'):
571
- loop = asyncio.get_event_loop()
572
- executor = ThreadPoolExecutor(max_workers=1)
573
- con = await loop.run_in_executor(executor, cls._connect, file)
574
- slf = cls(con, loop, executor)
575
- try:
576
- yield slf
577
- finally:
578
- await slf._asyncify(con.close)
579
-
580
- @staticmethod
581
- def _connect(file: Path) -> sqlite3.Connection:
582
- con = sqlite3.connect(str(file))
583
- con = logfire.instrument_sqlite3(con)
584
- cur = con.cursor()
585
- cur.execute(
586
- 'CREATE TABLE IF NOT EXISTS messages (id INT PRIMARY KEY, message_list TEXT);'
587
- )
588
- con.commit()
589
- return con
590
-
591
- async def add_messages(self, messages: bytes):
592
- await self._asyncify(
593
- self._execute,
594
- 'INSERT INTO messages (message_list) VALUES (?);',
595
- messages,
596
- commit=True,
597
- )
598
- await self._asyncify(self.con.commit)
599
-
600
- async def get_messages(self) -> list[ModelMessage]:
601
- c = await self._asyncify(
602
- self._execute, 'SELECT message_list FROM messages order by id asc'
603
- )
604
- rows = await self._asyncify(c.fetchall)
605
- messages: list[ModelMessage] = []
606
- for row in rows:
607
- messages.extend(ModelMessagesTypeAdapter.validate_json(row[0]))
608
- return messages
609
-
610
- def _execute(
611
- self, sql: LiteralString, *args: Any, commit: bool = False
612
- ) -> sqlite3.Cursor:
613
- cur = self.con.cursor()
614
- cur.execute(sql, args)
615
- if commit:
616
- self.con.commit()
617
- return cur
618
-
619
- async def _asyncify(
620
- self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs
621
- ) -> R:
622
- return await self._loop.run_in_executor( # type: ignore
623
- self._executor,
624
- partial(func, **kwargs),
625
- *args, # type: ignore
626
- )
627
-
628
-
629
- if __name__ == '__main__':
630
- import uvicorn
631
- uvicorn.run(
632
- 'app:app', reload=True, host="0.0.0.0", port=8000, reload_dirs=[str(THIS_DIR)]
633
  )
 
1
+ from __future__ import annotations as _annotations
2
+ import os
3
+ import asyncio
4
+ import json
5
+ import sqlite3
6
+ import datetime
7
+ import fastapi
8
+ import logfire
9
+ import time
10
+ from collections.abc import AsyncIterator
11
+ from concurrent.futures.thread import ThreadPoolExecutor
12
+ from contextlib import asynccontextmanager
13
+ from dataclasses import dataclass
14
+ from datetime import datetime, timezone, date
15
+ from functools import partial
16
+ from pathlib import Path
17
+ from typing import Annotated, Any, Callable, Literal, TypeVar
18
+
19
+ from pydantic import BaseModel, Field, ValidationError, model_validator
20
+ from typing import List, Optional, Dict
21
+
22
+ from fastapi import Depends, Request
23
+ from fastapi.responses import FileResponse, Response, StreamingResponse
24
+ from typing_extensions import LiteralString, ParamSpec, TypedDict
25
+
26
+ from pydantic_ai import Agent, ModelRetry, RunContext
27
+
28
+ from pydantic_ai.exceptions import UnexpectedModelBehavior
29
+ from pydantic_ai.messages import (
30
+ ModelMessage,
31
+ ModelMessagesTypeAdapter,
32
+ ModelRequest,
33
+ ModelResponse,
34
+ TextPart,
35
+ UserPromptPart,
36
+ )
37
+ from pydantic_ai.models.openai import OpenAIModel
38
+ #from pydantic_ai import OpenAIEmbeddings
39
+
40
+ #from sentence_transformers import SentenceTransformer
41
+ #embedding_model = SentenceTransformer("all-mpnet-base-v2")
42
+
43
+ class EmbeddingRequest(BaseModel):
44
+ text: str
45
+
46
+ class EmbeddingResponse(BaseModel):
47
+ embeddings: List[float]
48
+
49
+ abc = """
50
+ Anda adalah pelayan yang ramah, mampu memberikan info tentang Toko Teh "Arafatea", yaitu toko yang menjual 4 Kategori Produk yaitu (1) Teh Premium, (2) Teh Herbal, (3) Camilan/Snack dan (4) Buah Kering. Untuk Kategori Teh Premium ada 5 produk yaitu Teh Hitam, Teh Hijau, Teh Putih, Teh Genmaicha dan Matcha. Dalam kategori Teh Herbal ada 5 produk yaitu Pandan Tea, Mint Tea, Orange Tea, Teh Buah, dan Teh Rempah. Untuk kategori "Snack" meliputi 4 produk yaitu Greentea-Chocobar, Coffee-Chocobar, Rice-Cracker-Greentea, Rice-Cracker-Coffee; Untuk kategori "Buah Kering" meliputi 8 nama produk meliputi Buah Kering Mangga, Buah Kering Nanas, Buah Kering Lemon, dan Buah Kering Strawberry; Jus Mangga Kering, Jus Nanas Kering, Jus Buah Naga Kering, dan Jus Stawberry Kering. Tidak ada produk selain produk yang masuk 4 kategori tersebut. Alamat website www.arafatea.com, atau www.shopee.co.id/arafatea nomor telepon toko Arafatea adalah 08170218404
51
+ """
52
+
53
+
54
+
55
+
56
+ #await onFetchResponse(response)
57
+ #get_embeddings({"text":abc})
58
+
59
+
60
+ model = OpenAIModel(
61
+ 'gemma-2-2b-it', #'llama-3.2-1b-instruct',
62
+ base_url='http://localhost:1234/v1',
63
+ api_key='your-local-api-key',
64
+ )
65
+ model2 = OpenAIModel(
66
+ 'gemma-2-2b-it', #'llama-3.2-1b-instruct',
67
+ base_url='http://localhost:52199/api/v1/openai',
68
+ api_key='D6K06DS-P474SK6-P2ACYYC-K5516S1',
69
+ )
70
+ model1 = OpenAIModel(
71
+ 'gemma-2-2b-it', #'llama-3.2-1b-instruct',
72
+ base_url='http://localhost:52199/api/v1',
73
+ api_key='D6K06DS-P474SK6-P2ACYYC-K5516S1',
74
+ )
75
+
76
+ # Model Pydantic untuk Sales Order
77
+ class product1(BaseModel):
78
+ ds_productCode: Optional[str] = "" #str = Field(..., min_length=1, max_length=20)
79
+ ds_productName: str = Field(..., min_length=1, max_length=100)
80
+ ds_quantity: int = Field(..., ge=1)
81
+ ds_unitPrice: float = Field(..., ge=0)
82
+ ds_itemAmt: Optional[float] = 0
83
+
84
+ # 'if-token-present' means nothing will be sent (and the example will work) if you don't have logfire configured
85
+ logfire.configure(send_to_logfire='if-token-present')
86
+
87
+ class product(BaseModel):
88
+ ds_productCode: Optional[str] = "" #str = Field(..., min_length=1, max_length=20)
89
+ ds_productName: Optional[str] = ""
90
+ ds_quantity: Optional[int] = 0
91
+ ds_unitPrice: Optional[float] = 0
92
+ ds_itemAmt: Optional[float] = 0
93
+
94
+ class SalesOrder(BaseModel):
95
+ ds_salesOrderId: Optional[str] = ""
96
+ ds_salesDate: Optional[str] = ""
97
+ ds_customerName: str = Field(..., min_length=1, max_length=100)
98
+ ds_customerAddress: Optional[str] = ""
99
+ ds_items: List[product] = Field(..., min_items=1)
100
+ ds_shippingCost:Optional[float] = 0
101
+ ds_shippingAddress: Optional[str] = ""
102
+ ds_totalAmount:Optional[float] = 0
103
+ #ds_paymentTerms: Optional[str] = ""
104
+ os.environ['GEMINI_API_KEY'] = 'AIzaSyAsVIHsPIIfDBTb2K6VNdNlMt05t8x3mtE'
105
+ #agent= Agent('gemini-1.5-flash', result_type=SalesOrder)
106
+
107
+ # # Create a system prompt to guide the model
108
+ SYSTEM_PROMPT = """
109
+ Anda adalah pelayan yang ramah, mampu memberikan info tentang Toko Teh "Arafatea", yaitu toko yang menjual 4 Kategori Produk yaitu (1) Teh Premium, (2) Teh Herbal, (3) Camilan/Snack dan (4) Buah Kering. Untuk Kategori Teh Premium ada 5 produk yaitu P11-Teh Hitam, P12-Teh Hijau, P13-Teh Putih, P14-Teh Genmaicha dan P15-Matcha (Serbuk Teh Hijau). Dalam kategori Teh Herbal ada 5 produk yaitu P21-Pandan Tea, P22-Mint Tea, P23-Orange Tea, P24-Teh Buah, dan P25-Teh Rempah. Untuk kategori "Snack" meliputi 4 produk yaitu P31-Greentea-Chocobar, P32-Coffee-Chocobar, P33-Rice-Cracker-Greentea, P34-Rice-Cracker-Coffee; Untuk kategori "Buah Kering" meliputi 8 nama produk meliputi P41 Buah Mangga Kering, P42-Buah Nanas Kering, P43-Buah Lemon Kering, dan P44-Buah Strawberry Kering; P45-Jus Mangga Kering, P46-Jus Nanas Kering, P47-Jus Buah Naga Kering, dan P48-Jus Stawberry Kering. Tidak ada produk selain produk yang masuk 4 kategori tersebut. Alamat website www.arafatea.com, atau www.shopee.co.id/arafatea nomor telepon toko Arafatea adalah 08170218404.
110
+
111
+ Berikut ini adalah informasi tentang masing-masing produk.
112
+
113
+ P11-Teh Hitam, adalah teh hitam kualitas terbaik, ditanam di dataran tinggi Bandung dengan hara tanah baik, menghasilkan teh hitam berkualitas.
114
+ P12-Teh Hijau, adalah teh hijau kualitas terbaik, ditanam di dataran tinggi Bandung dengan hara tanah baik, menghasilkan teh hijau berkualitas.
115
+ P13-Teh Putih, adalah teh putih yang dibuat hanya dari pucuk daun teh yang belum mekar. Dikeringkan dengan mesin dehydrator pada suhu rendah, secara hygienis dan otomatis.
116
+ P14-Teh Genmaicha, adalah teh hijau yang dihasilkan dari varietas tanaman Teh Yabugita (Japanese style), terkenal karena rasanya yang enak tanpa rasa sepet/pahit.
117
+ P15-Matcha (Serbuk Teh Hijau), adalah daun teh hijau kualitas terbaik yang dijadikan serbuk, biasa dinikmati sebagai macha murni dan matcha latte (dengan tambahan krimer)
118
+
119
+ P21 Pandan Tea, adalah sinergi Teh hijau ditambah daun pandan, menghasilkan teh hijau berkualitas dengan aroma harum dari daun pandan.
120
+ P22 Mint Tea, adalah sinergi Teh hijau dan daun mint yang menyegarkan.
121
+ P23 Orange Tea, adalah sinergi Teh hitam dan irisan buah jeruk, rasanya enak menyegarkan khas teh buah jeruk.
122
+ P24 Teh Buah, adalah teh hitam yang bersinergi dengan beberapaa macam buah termasuk lemon dan nanas.
123
+ P25 Teh Rempah, adalah teh hijau dengan campuran herbal untuk meningkatkan imunitas, terutama untuk menghadapi cuaca hujan dan konsidi polusi.
124
+
125
+ P31-Greentea-Chocobar, adalah coklat berbentuk batang dengan dilapisi teh hijau terbaik, mengandung zat aktif l-theanine dalaam dosis cukup menenangkan.
126
+ P32-Coffee-Chocobar, adalah coklat batang dilapisi serbuk kopi, enak menyegarkan.
127
+ P33-Rice-Cracker-Greentea, adalah nasi ketan yang dilapisi teh hijau, sungguh enak sekali, sumber energi dan meningkatkan fokus.
128
+ P34-Rice-Cracker-Coffee, adalah sumber energi karena mengandung karbohidrat dari nasi ketan, dan kesegaran dari kopi.
129
+
130
+ P41 Buah Mangga Kering, berupa irisan tipis buah mangga, dikeringkan dengan mesin dehydrator suhu rendah. Asli buah, tanpa tambahan apapun, 100 persen alami, sumber serat dan vitamin.
131
+
132
+ P42 Buah Nanas Kering, berupa irisan tipis buah nanas, dikeringkan dengan mesin dehydrator suhu rendah. Asli buah, tanpa tambahan apapun, 100 persen alami, sumber serat dan vitamin.
133
+
134
+ P43 Buah Naga Kering, berupa irisan tipis buah naga, dikeringkan dengan mesin dehydrator suhu rendah. Asli buah, tanpa tambahan apapun, 100 persen alami, sumber serat dan vitamin.
135
+
136
+ P44 Buah Strawberry Kering, berupa irisan tipis buah Strawberry, dikeringkan dengan mesin dehydrator suhu rendah. Asli buah, tanpa tambahan apapun, 100 persen alami, sumber serat dan vitamin.
137
+
138
+ P45 Jus Mangga Kering, biasa disebut Manggo fruit leather, dibuat dari buah Mangga yang matang di pohon, dibersihkan dan di-jus, kemudian dikeringkan dengan mesin dehydrator suhu rendah. Hasilnya jus buah Mangga yang kering berbentuk lembaran, awet dan 100 persen alami.
139
+
140
+ P46 Jus Nanas Kering, biasa disebut Pineapple fruit leather, dibuat dari buah Nanas yang matang di pohon, dibersihkan dan di-jus, kemudian dikeringkan dengan mesin dehydrator suhu rendah. Hasilnya jus buah Nanas yang kering berbentuk lembaran, awet dan 100 persen alami.
141
+
142
+ P47 Jus Buah Naga Kering, biasa disebut Dragon fruit leather, dibuat dari buah Naga yang matang di pohon, dibersihkan dan di-jus, kemudian dikeringkan dengan mesin dehydrator suhu rendah. Hasilnya buah Naga kering berbentuk lembaran, awet dan 100 persen alami.
143
+
144
+ P48 Jus Strawberry Kering, biasa disebut Strawberry fruit leather, dibuat dari buah strawberry yang matang di pohon, dibersihkan dan di-jus, kemudian dikeringkan dengan mesin dehydrator suhu rendah. Hasilnya strawberry kering berbentuk lembaran, awet dan 100 persen alami.
145
+ """
146
+ #agent3 = Agent(model=ollama_model, result_type=PetList, retries=3, system_prompt=SYSTEM_PROMPT)
147
+ #agent3 = Agent(model=ollama_model, retries=3, system_prompt=SYSTEM_PROMPT)
148
+
149
+
150
+ #INI SAJA. SALAH SATU
151
+ agent = Agent('gemini-1.5-flash', system_prompt=SYSTEM_PROMPT) # OK-Gemini
152
+ #agent = Agent(model) # OK-Lokal
153
+ #agent = Agent(model, system_prompt=SYSTEM_PROMPT) # OK-Lokal
154
+
155
+ THIS_DIR = Path(__file__).parent
156
+
157
+
158
+ @asynccontextmanager
159
+ async def lifespan(_app: fastapi.FastAPI):
160
+ async with Database.connect() as db:
161
+ yield {'db': db}
162
+
163
+
164
+ app = fastapi.FastAPI(lifespan=lifespan)
165
+ logfire.instrument_fastapi(app)
166
+
167
+
168
+ @app.get('/')
169
+ async def index() -> FileResponse:
170
+ print(THIS_DIR / 'produk.html')
171
+ return FileResponse((THIS_DIR / 'produk.html'), media_type='text/html')
172
+
173
+
174
+ @app.get('/chat_app.ts')
175
+ async def main_ts() -> FileResponse:
176
+ """Get the raw typescript code, it's compiled in the browser, forgive me."""
177
+ return FileResponse((THIS_DIR / 'chat_app.ts'), media_type='text/plain')
178
+
179
+
180
+ async def get_db(request: Request) -> Database:
181
+ return request.state.db
182
+
183
+ @app.get('/chat/')
184
+ async def get_chat(database: Database = Depends(get_db)) -> Response:
185
+ msgs = await database.get_messages()
186
+ return Response(
187
+ b'\n'.join(json.dumps(to_chat_message(m)).encode('utf-8') for m in msgs),
188
+ media_type='text/plain',
189
+ )
190
+
191
+ @app.get('/embed/')
192
+ async def get_embeddings(request: EmbeddingRequest):
193
+ print(request.text)
194
+ embeddings_result = await embedding_model.encode(request.text)
195
+ abcde = embeddings_result.tolist()
196
+ print(abcde)
197
+ return EmbeddingResponse(embeddings=embeddings_result.tolist())
198
+
199
+ class ChatMessage(TypedDict):
200
+ """Format of messages sent to the browser."""
201
+ role: Literal['user', 'model']
202
+ timestamp: str
203
+ content: str
204
+
205
+
206
+ def to_chat_message(m: ModelMessage) -> ChatMessage:
207
+ first_part = m.parts[0]
208
+ if isinstance(m, ModelRequest):
209
+ if isinstance(first_part, UserPromptPart):
210
+ return {
211
+ 'role': 'user',
212
+ 'timestamp': first_part.timestamp.isoformat(),
213
+ 'content': first_part.content,
214
+ }
215
+ elif isinstance(m, ModelResponse):
216
+ if isinstance(first_part, TextPart):
217
+ return {
218
+ 'role': 'model',
219
+ 'timestamp': m.timestamp.isoformat(),
220
+ 'content': first_part.content,
221
+ }
222
+ raise UnexpectedModelBehavior(f'Unexpected message type for chat app: {m}')
223
+
224
+ def to_ds_message(m: ModelMessage) -> ChatMessage:
225
+
226
+ if isinstance(m, ModelRequest):
227
+ first_part = m.parts[0]
228
+ if isinstance(first_part, UserPromptPart):
229
+ return {
230
+ 'role': 'user',
231
+ 'timestamp': first_part.timestamp.isoformat(),
232
+ 'content': first_part.content,
233
+ }
234
+ elif isinstance(m, ModelResponse):
235
+ first_part = m.parts[0]
236
+ if isinstance(first_part, TextPart):
237
+ return {
238
+ 'role': 'model',
239
+ 'timestamp': m.timestamp.isoformat(),
240
+ 'content': first_part.content,
241
+ }
242
+ raise UnexpectedModelBehavior(f'Unexpected ds-message type for chat app: {m}')
243
+
244
+
245
+
246
+
247
+ @app.post('/chat1/')
248
+ async def post_chat1(
249
+ prompt: Annotated[str, fastapi.Form()], database: Database = Depends(get_db)
250
+ ) -> StreamingResponse:
251
+ async def stream_messages():
252
+ """Streams new line delimited JSON `Message`s to the client."""
253
+ # stream the user prompt so that can be displayed straight away
254
+ yield (
255
+ json.dumps(
256
+ {
257
+ 'role': 'user',
258
+ 'timestamp': datetime.now(tz=timezone.utc).isoformat(),
259
+ 'content': prompt,
260
+ }
261
+ ).encode('utf-8')
262
+ + b'\n'
263
+ )
264
+ # get the chat history so far to pass as context to the agent
265
+ messages = await database.get_messages()
266
+ # run the agent with the user prompt and the chat history
267
+ async with agent.run_stream(prompt, message_history=messages) as result:
268
+ async for text in result.stream(debounce_by=0.01):
269
+ # text here is a `str` and the frontend wants
270
+ # JSON encoded ModelResponse, so we create one
271
+ m = ModelResponse.from_text(content=text, timestamp=result.timestamp())
272
+ yield json.dumps(to_chat_message(m)).encode('utf-8') + b'\n'
273
+
274
+ # add new messages (e.g. the user prompt and the agent response in this case) to the database
275
+ #print("---",result.new_messages_json(),"---")
276
+ #print("***",prompt,"***")
277
+ #await database.add_messages(result.new_messages_json())
278
+ print("** selesai **")
279
+ return StreamingResponse(stream_messages(), media_type='text/plain')
280
+
281
+ @app.post('/chat/')
282
+ async def post_chat(
283
+ prompt: Annotated[str, fastapi.Form()], database: Database = Depends(get_db)
284
+ ) -> StreamingResponse:
285
+ async def stream_messages():
286
+ """Streams new line delimited JSON `Message`s to the client."""
287
+ # stream the user prompt so that can be displayed straight away
288
+ yield (
289
+ json.dumps(
290
+ {
291
+ 'role': 'user',
292
+ 'timestamp': datetime.now(tz=timezone.utc).isoformat(),
293
+ 'content': prompt,
294
+ }
295
+ ).encode('utf-8')
296
+ + b'\n'
297
+ )
298
+ # get the chat history so far to pass as context to the agent
299
+ messages = await database.get_messages()
300
+ # run the agent with the user prompt and the chat history
301
+ async with agent.run_stream(prompt, message_history=messages) as result:
302
+ async for text in result.stream(debounce_by=0.01):
303
+ # text here is a `str` and the frontend wants
304
+ # JSON encoded ModelResponse, so we create one
305
+ m = ModelResponse.from_text(content=text, timestamp=result.timestamp())
306
+ yield json.dumps(to_chat_message(m)).encode('utf-8') + b'\n'
307
+
308
+ # add new messages (e.g. the user prompt and the agent response in this case) to the database
309
+ #print("---",result.new_messages_json(),"---")
310
+ #print("***",prompt,"***")
311
+ #await database.add_messages(result.new_messages_json())
312
+ async def ds_messages(prompt1):
313
+ try:
314
+ #prompt2=f"Ekstrak data Sales Order dari teks: {prompt1}. Hari ini adalah tanggal {date.today()}"
315
+ #prompt2=f"{prompt1}. Hari ini adalah tanggal {date.today()}"
316
+ prompt2=f"{prompt1}"
317
+ yield (
318
+ json.dumps(
319
+ {
320
+ 'role': 'user',
321
+ 'timestamp': datetime.now(tz=timezone.utc).isoformat(),
322
+ 'content': prompt1,
323
+ }
324
+ ).encode('utf-8')
325
+ + b'\n'
326
+ )
327
+ #messages = await database.get_messages()
328
+ async with agent.run_stream(prompt2) as result:
329
+ async for text in result.stream(debounce_by=0.1):
330
+ m = ModelResponse.from_text(content=text, timestamp=result.timestamp())
331
+ yield json.dumps(to_ds_message(m)).encode('utf-8') + b'\n'
332
+
333
+ ##print(result.usage())
334
+ await database.add_messages(result.new_messages_json())
335
+ return
336
+ darso = json.loads(result.new_messages_json())
337
+ #print(darso)
338
+ darso0= darso[0]
339
+ #darso0.pop(darso0['parts'][0])
340
+ #print("00|------------------")
341
+ #print("01|", darso0 )
342
+ #print("01a|", darso0['parts'][0] )
343
+ #print("01b|", darso0['parts'][1] )
344
+ darso0['parts'][0] = darso0['parts'][1]
345
+ #print("01?|", darso0 )
346
+ #print("01??|", darso )
347
+ #print("02|------------------")
348
+ darso1= darso[1]
349
+ #print("1|", darso1)
350
+ darso2= json.loads(json.dumps(darso1))
351
+ #print("2|",darso2['parts'][0])
352
+ darso3= darso2['parts'][0]
353
+ darso4= json.loads(json.dumps(darso3))
354
+ #print("4|",darso4['content'])
355
+ darso5= darso4['content']
356
+ darso5=darso5.split('```', 2)
357
+ darso5=darso5[1]
358
+ #print("5a|",darso5)
359
+ darso5=darso5.replace('json', '')
360
+ print("5|",darso5,"|")
361
+ try:
362
+ darso6= json.loads(darso5) #json
363
+ darso7= SalesOrder.model_validate(darso6)
364
+ except:
365
+ darso6= "ERR"
366
+ print("6|",darso6,"|")
367
+ if "ds_items" in darso5:
368
+ cek_str="ds_items"
369
+ else:
370
+ cek_str="--"
371
+ if darso6=="ERR":
372
+ ds_id = time.time()
373
+ ds_salesOrderId = "ERR"
374
+ ds_salesDate = 'ERR'
375
+ ds_customerName="-"
376
+ ds_customerAddress="-"
377
+
378
+ ds_productName1 = "Produk1 --- "
379
+ ds_quantity1 = 1
380
+ ds_unitPrice1 = 0
381
+ ds_itemAmt1 = 0
382
+
383
+ ds_productName2 = "Produk2 --- "
384
+ ds_quantity2 = 0
385
+ ds_unitPrice2 = 0
386
+ ds_itemAmt2 = 0
387
+
388
+ ds_productName3 = "Produk3 --- "
389
+ ds_quantity3 = 0
390
+ ds_unitPrice3 = 0
391
+ ds_itemAmt3 = 0
392
+ ds_shippingAddress=""
393
+ ds_shippingCost=0
394
+ ds_totalAmount=0
395
+ else:
396
+ ds_id = time.time()
397
+ ds_salesOrderId = "OK"
398
+ ds_salesDate = 'OK'
399
+ try:
400
+ ds_salesOrderId = darso7.ds_salesOrderId
401
+ print("7|ds_salesOrderId")
402
+ ds_salesDate = darso7.ds_salesDate
403
+ print("7|ds_salesDate")
404
+ ds_customerName=f"""{darso7.ds_customerName}"""
405
+ print("7|ds_customerName:",ds_customerName)
406
+ ds_customerAddress=f"""{darso7.ds_customerAddress}"""
407
+ print("7|ds_customerAddress:", len(darso7.ds_items))
408
+ ds_productName1 = darso7.ds_items[0].ds_productName
409
+ print("7|ds_productName1")
410
+ ds_quantity1 = darso7.ds_items[0].ds_quantity
411
+ print("7|ds_quantity1")
412
+ ds_unitPrice1 = darso7.ds_items[0].ds_unitPrice
413
+ print("7|ds_unitPrice1")
414
+ ds_itemAmt1 = darso7.ds_items[0].ds_itemAmt
415
+ print("7|ds_itemAmt1")
416
+ ds_productName2 = "-"
417
+ ds_quantity2 = 0
418
+ ds_unitPrice2 = 0
419
+ ds_itemAmt2 = 0
420
+ ds_productName3 = "-"
421
+ ds_quantity3 = 0
422
+ ds_unitPrice3 = 0
423
+ ds_itemAmt3 = 0
424
+
425
+ if len(darso7.ds_items)>1:
426
+ ds_productName2 = darso7.ds_items[1].ds_productName
427
+ ds_quantity2 = darso7.ds_items[1].ds_quantity
428
+ ds_unitPrice2 = darso7.ds_items[1].ds_unitPrice
429
+ ds_itemAmt2 = darso7.ds_items[1].ds_itemAmt
430
+ if len(darso7.ds_items)>2:
431
+ ds_productName3 = darso7.ds_items[2].ds_productName
432
+ ds_quantity3 = darso7.ds_items[2].ds_quantity
433
+ ds_unitPrice3 = darso7.ds_items[2].ds_unitPrice
434
+ ds_itemAmt3 = darso7.ds_items[2].ds_itemAmt
435
+
436
+ ds_shippingCost=darso7.ds_shippingCost
437
+ print("7|ds_shippingCost")
438
+ ds_shippingAddress=f"""{darso7.ds_shippingAddress}"""
439
+ print("7|ds_shippingAddress")
440
+ ds_totalAmount=darso7.ds_totalAmount
441
+ print("7|ds_totalAmount")
442
+ except:
443
+ ds_salesOrderId = "OK2"
444
+ ds_salesDate = 'OK2'
445
+ ds_customerName="-"
446
+ ds_customerAddress="-"
447
+
448
+ ds_productName1 = "Produk1"
449
+ ds_quantity1 = 0
450
+ ds_unitPrice1 = 0
451
+ ds_itemAmt1 = 0
452
+
453
+ ds_productName2 = "Produk2"
454
+ ds_quantity2 = 0
455
+ ds_unitPrice2 = 0
456
+ ds_itemAmt2 = 0
457
+
458
+ ds_productName3 = "Produk3"
459
+ ds_quantity3 = 0
460
+ ds_unitPrice3 = 0
461
+ ds_itemAmt3 = 0
462
+ ds_shippingAddress=""
463
+ ds_shippingCost=0
464
+ ds_totalAmount=0
465
+
466
+ formDs = f"""
467
+
468
+ <form id="myaiForm{ds_id}" action="javascript:abcde({ds_id});" class="form-container">
469
+ <h3>Pesanan</h3>
470
+ <table>
471
+ <tr>
472
+ <td><label for="ds_salesOrderId"><b>SO#</b></label><input type="text" placeholder="" name="ds_salesOrderId" value="{ds_salesOrderId}"></td>
473
+ <td><label for="ds_salesDate"><b>Date</b></label><input type="text" placeholder="" name="ds_salesDate" value="{ds_salesDate}"></td>
474
+ </tr>
475
+ <tr>
476
+ <td colspan="2"><label for="ds_customerName"><b>Customer</b></label><input type="text" placeholder="" name="ds_customerName" value="{ds_customerName}"></td>
477
+ </tr>
478
+ <tr>
479
+ <td colspan="2"><label for="ds_customerAddress"><b>Alamat</b></label><input type="text" placeholder="" name="ds_customerAddress" value="{ds_customerAddress}"></td>
480
+ </tr>
481
+ </table style="width:100%">
482
+ <b>Item Barang:</b>
483
+ <table>
484
+ <tr><th>Prod</th><th style="text-align: center;">Qty</th><th style="text-align: center;">Prc</th><th style="text-align: center;">Rp</th></tr>
485
+ <tr>
486
+ <td><input type="text" placeholder="-" name="ds_productName1" value="{ds_productName1}"></td>
487
+ <td><input type="text" style="text-align: center;" name="ds_quantity1" value={ds_quantity1}></td>
488
+ <td><input type="text" style="text-align: center;" name="ds_unitPrice1" value={ds_unitPrice1}></td>
489
+ <td><input type="text" style="text-align: center;" name="ds_itemAmt1" value={ds_itemAmt1}></td>
490
+ </tr>
491
+ <tr>
492
+ <td><input type="text" placeholder="-" name="ds_productName2" value="{ds_productName2}"></td>
493
+ <td><input type="text" style="text-align: center;" name="ds_quantity2" value={ds_quantity2}></td>
494
+ <td><input type="text" style="text-align: center;" name="ds_unitPrice2" value={ds_unitPrice2}></td>
495
+ <td><input type="text" style="text-align: center;" name="ds_itemAmt2" value={ds_itemAmt2}></td>
496
+ </tr>
497
+ <tr>
498
+ <td><input type="text" placeholder="-" name="ds_productName3" value="{ds_productName3}"></td>
499
+ <td><input type="text" style="text-align: center;" name="ds_quantity3" value={ds_quantity3}></td>
500
+ <td><input type="text" style="text-align: center;" name="ds_unitPrice3" value={ds_unitPrice3}></td>
501
+ <td><input type="text" style="text-align: center;" name="ds_itemAmt3" value={ds_itemAmt3}></td>
502
+ </tr>
503
+ </table>
504
+ <table>
505
+ <tr>
506
+ <td style="text-align: center;"><b>Ongkir</b></td>
507
+ <td style="text-align: center;"><b>Total</b></td>
508
+ </tr>
509
+ <tr>
510
+ <td><input type="text" style="text-align: center;" placeholder="0" name="ds_shippingCost" value={ds_shippingCost}></td>
511
+ <td><input type="text" style="text-align: center;" placeholder="0" name="ds_totalAmount" value={ds_totalAmount}></td>
512
+ </tr>
513
+ <tr>
514
+ <td colspan="2"><label for="ds_shippingAddress"><b></b></label><input type="text" placeholder="" name="ds_shippingAddress" value="{ds_shippingAddress}"></td>
515
+ </tr>
516
+ </table>
517
+ <button type="submit" class="btn">Submit</button>
518
+ <button type="button" class="btn cancel" onclick="closeAiForm({ds_id})">Close</button>
519
+ </form>
520
+ <form id="myaiForm2{ds_id}" class="form-container" style="display:none;">
521
+ <button type="button" class="btn umum" onclick="openAiForm({ds_id})">Open Form</button>
522
+ </form>
523
+ """
524
+ m = ModelResponse.from_text(content=formDs, timestamp=result.timestamp())
525
+ yield json.dumps(to_ds_message(m)).encode('utf-8') + b'\n'
526
+ print("OK:")
527
+ #await database.add_messages(result.new_messages_json())
528
+ await database.add_messages(json.dumps(darso))
529
+ ##print(len(items))
530
+ #darso7 = SalesOrder.model_validate(darso6)
531
+ #print("[--",darso7.ds_customerName,"--]")
532
+ #darso8 = darso7.ds_items[0]
533
+ ##, len(darso7.ds_items)
534
+ #print("[--",darso8.ds_productName,"--]")
535
+ except ValueError as e:
536
+ print(e)
537
+ if prompt[0] == "@" :
538
+ #print("@@@", prompt, "@@@")
539
+ nn = len(prompt)
540
+ prompt = prompt[1:nn]
541
+ print(">>>", prompt, "<<<")
542
+ return StreamingResponse(ds_messages(prompt), media_type='text/plain')
543
+ elif prompt[0] != "@" :
544
+ #print("biasa")
545
+ return StreamingResponse(stream_messages(), media_type='text/plain')
546
+ print("** selesai **")
547
+ return StreamingResponse(stream_messages(), media_type='text/plain')
548
+
549
+ P = ParamSpec('P')
550
+ R = TypeVar('R')
551
+
552
+
553
+ @dataclass
554
+ class Database:
555
+ """Rudimentary database to store chat messages in SQLite.
556
+
557
+ The SQLite standard library package is synchronous, so we
558
+ use a thread pool executor to run queries asynchronously.
559
+ """
560
+
561
+ con: sqlite3.Connection
562
+ _loop: asyncio.AbstractEventLoop
563
+ _executor: ThreadPoolExecutor
564
+
565
+ @classmethod
566
+ @asynccontextmanager
567
+ async def connect(
568
+ cls, file: Path = THIS_DIR / '.chat_messages.sqlite'
569
+ ) -> AsyncIterator[Database]:
570
+ with logfire.span('connect to DB'):
571
+ loop = asyncio.get_event_loop()
572
+ executor = ThreadPoolExecutor(max_workers=1)
573
+ con = await loop.run_in_executor(executor, cls._connect, file)
574
+ slf = cls(con, loop, executor)
575
+ try:
576
+ yield slf
577
+ finally:
578
+ await slf._asyncify(con.close)
579
+
580
+ @staticmethod
581
+ def _connect(file: Path) -> sqlite3.Connection:
582
+ con = sqlite3.connect(str(file))
583
+ con = logfire.instrument_sqlite3(con)
584
+ cur = con.cursor()
585
+ cur.execute(
586
+ 'CREATE TABLE IF NOT EXISTS messages (id INT PRIMARY KEY, message_list TEXT);'
587
+ )
588
+ con.commit()
589
+ return con
590
+
591
+ async def add_messages(self, messages: bytes):
592
+ await self._asyncify(
593
+ self._execute,
594
+ 'INSERT INTO messages (message_list) VALUES (?);',
595
+ messages,
596
+ commit=True,
597
+ )
598
+ await self._asyncify(self.con.commit)
599
+
600
+ async def get_messages(self) -> list[ModelMessage]:
601
+ c = await self._asyncify(
602
+ self._execute, 'SELECT message_list FROM messages order by id asc'
603
+ )
604
+ rows = await self._asyncify(c.fetchall)
605
+ messages: list[ModelMessage] = []
606
+ for row in rows:
607
+ messages.extend(ModelMessagesTypeAdapter.validate_json(row[0]))
608
+ return messages
609
+
610
+ def _execute(
611
+ self, sql: LiteralString, *args: Any, commit: bool = False
612
+ ) -> sqlite3.Cursor:
613
+ cur = self.con.cursor()
614
+ cur.execute(sql, args)
615
+ if commit:
616
+ self.con.commit()
617
+ return cur
618
+
619
+ async def _asyncify(
620
+ self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs
621
+ ) -> R:
622
+ return await self._loop.run_in_executor( # type: ignore
623
+ self._executor,
624
+ partial(func, **kwargs),
625
+ *args, # type: ignore
626
+ )
627
+
628
+
629
+ if __name__ == '__main__':
630
+ import uvicorn
631
+ uvicorn.run(
632
+ 'app:app', reload=True, host="0.0.0.0", port=8000, reload_dirs=[str(THIS_DIR)]
633
  )