T7yrttrd commited on
Commit
f4c491f
·
verified ·
1 Parent(s): ef8df51

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +220 -2
app.py CHANGED
@@ -133,7 +133,7 @@ STRICT IDENTITY RULES:
133
 
134
  3. NATURAL CONVERSATION & TOOL RESPONSE STYLE:
135
  - Do NOT introduce your company, founder, or model name in general responses unless requested.
136
- - Always summarize and format Google Connector execution results into clean, polite, and natural language automatically matching the language used by the user.
137
  - Never output raw technical logs, IDs, or developer headers directly to the user."""
138
 
139
  TOOLS = [
@@ -358,6 +358,65 @@ TOOLS = [
358
  "required": ["file_id", "content"]
359
  }
360
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  }
362
  ]
363
 
@@ -444,6 +503,157 @@ async def get_google_creds(user_id: str, service_name: str = "google"):
444
  pass
445
  return creds
446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  async def gmail_search_emails(user_id: str, query: str = "", max_results: int = 5):
448
  creds = await get_google_creds(user_id, "gmail")
449
  if not creds:
@@ -724,6 +934,14 @@ async def execute_tool_call(user_id: str, func_name: str, args: dict) -> str:
724
  return await drive_delete_file(user_id, args.get("file_id", ""))
725
  elif func_name == "drive_update_file":
726
  return await drive_update_file(user_id, args.get("file_id", ""), args.get("content", ""))
 
 
 
 
 
 
 
 
727
  return "Unknown tool function."
728
 
729
  def count_tokens(messages):
@@ -932,7 +1150,7 @@ async def generate(request: Request):
932
 
933
  if content and not has_tool_call:
934
  yield json.dumps({"answer": content}) + "\n"
935
- me:
936
  continue
937
 
938
  if has_tool_call and tool_calls_acc:
 
133
 
134
  3. NATURAL CONVERSATION & TOOL RESPONSE STYLE:
135
  - Do NOT introduce your company, founder, or model name in general responses unless requested.
136
+ - Always summarize and format Google and Notion Connector execution results into clean, polite, and natural language automatically matching the language used by the user.
137
  - Never output raw technical logs, IDs, or developer headers directly to the user."""
138
 
139
  TOOLS = [
 
358
  "required": ["file_id", "content"]
359
  }
360
  }
361
+ },
362
+ {
363
+ "type": "function",
364
+ "function": {
365
+ "name": "notion_search",
366
+ "description": "Search Notion pages, databases, or documents by query",
367
+ "parameters": {
368
+ "type": "object",
369
+ "properties": {
370
+ "query": {"type": "string", "description": "Search keyword or empty to list recent pages"},
371
+ "max_results": {"type": "integer", "description": "Number of results to fetch, default 5"}
372
+ }
373
+ }
374
+ }
375
+ },
376
+ {
377
+ "type": "function",
378
+ "function": {
379
+ "name": "notion_get_page_content",
380
+ "description": "Read text content and blocks of a specific Notion page by page_id",
381
+ "parameters": {
382
+ "type": "object",
383
+ "properties": {
384
+ "page_id": {"type": "string", "description": "The ID of the Notion page"}
385
+ },
386
+ "required": ["page_id"]
387
+ }
388
+ }
389
+ },
390
+ {
391
+ "type": "function",
392
+ "function": {
393
+ "name": "notion_create_page",
394
+ "description": "Create a new page in Notion inside a parent page or database",
395
+ "parameters": {
396
+ "type": "object",
397
+ "properties": {
398
+ "parent_id": {"type": "string", "description": "Optional parent page or database ID"},
399
+ "title": {"type": "string", "description": "Title of the new Notion page"},
400
+ "content": {"type": "string", "description": "Initial text content for the page"}
401
+ },
402
+ "required": ["title"]
403
+ }
404
+ }
405
+ },
406
+ {
407
+ "type": "function",
408
+ "function": {
409
+ "name": "notion_append_block_children",
410
+ "description": "Append text or paragraph blocks to an existing Notion page by page_id",
411
+ "parameters": {
412
+ "type": "object",
413
+ "properties": {
414
+ "page_id": {"type": "string", "description": "The ID of the Notion page"},
415
+ "content": {"type": "string", "description": "Text content to append to the page"}
416
+ },
417
+ "required": ["page_id", "content"]
418
+ }
419
+ }
420
  }
421
  ]
422
 
 
503
  pass
504
  return creds
505
 
506
+ async def notion_get_headers(user_id: str):
507
+ token_data = await get_token_from_supabase(user_id, "notion")
508
+ if not token_data:
509
+ return None
510
+ access_token = token_data.get("access_token") or token_data.get("token")
511
+ if not access_token:
512
+ return None
513
+ return {
514
+ "Authorization": f"Bearer {access_token}",
515
+ "Notion-Version": "2022-06-28",
516
+ "Content-Type": "application/json"
517
+ }
518
+
519
+ async def notion_search(user_id: str, query: str = "", max_results: int = 5):
520
+ headers = await notion_get_headers(user_id)
521
+ if not headers:
522
+ return "Notion is not connected. Please connect Notion account."
523
+ try:
524
+ url = "https://api.notion.com/v1/search"
525
+ payload = {"page_size": max_results}
526
+ if query:
527
+ payload["query"] = query
528
+ async with httpx.AsyncClient() as c:
529
+ res = await c.post(url, headers=headers, json=payload)
530
+ if res.status_code != 200:
531
+ return f"Error searching Notion: {res.text}"
532
+ data = res.json()
533
+ results = data.get("results", [])
534
+ if not results:
535
+ return "No pages or databases found in Notion."
536
+ items = []
537
+ for item in results:
538
+ item_id = item.get("id", "")
539
+ obj_type = item.get("object", "page")
540
+ title = "Untitled"
541
+ if obj_type == "page":
542
+ props = item.get("properties", {})
543
+ for k, v in props.items():
544
+ if v.get("type") == "title":
545
+ t_arr = v.get("title", [])
546
+ if t_arr:
547
+ title = "".join([t.get("plain_text", "") for t in t_arr])
548
+ break
549
+ elif obj_type == "database":
550
+ t_arr = item.get("title", [])
551
+ if t_arr:
552
+ title = "".join([t.get("plain_text", "") for t in t_arr])
553
+ url_val = item.get("url", "")
554
+ items.append(f"Page/DB ID: {item_id} | Type: {obj_type} | Title: {title} | URL: {url_val}")
555
+ return "\n".join(items)
556
+ except Exception as e:
557
+ return f"Error searching Notion: {str(e)}"
558
+
559
+ async def notion_get_page_content(user_id: str, page_id: str):
560
+ headers = await notion_get_headers(user_id)
561
+ if not headers:
562
+ return "Notion is not connected."
563
+ try:
564
+ clean_id = page_id.replace("-", "").strip()
565
+ url = f"https://api.notion.com/v1/blocks/{clean_id}/children"
566
+ async with httpx.AsyncClient() as c:
567
+ res = await c.get(url, headers=headers)
568
+ if res.status_code != 200:
569
+ return f"Error reading Notion page content: {res.text}"
570
+ data = res.json()
571
+ results = data.get("results", [])
572
+ if not results:
573
+ return "Notion page is empty."
574
+ texts = []
575
+ for b in results:
576
+ b_type = b.get("type")
577
+ if b_type and b_type in b and "rich_text" in b[b_type]:
578
+ rt = b[b_type].get("rich_text", [])
579
+ t = "".join([x.get("plain_text", "") for x in rt])
580
+ if t:
581
+ texts.append(t)
582
+ return "\n".join(texts) if texts else "Page contains non-text blocks."
583
+ except Exception as e:
584
+ return f"Error reading Notion page: {str(e)}"
585
+
586
+ async def notion_create_page(user_id: str, parent_id: str = None, title: str = "Untitled", content: str = ""):
587
+ headers = await notion_get_headers(user_id)
588
+ if not headers:
589
+ return "Notion is not connected."
590
+ try:
591
+ url = "https://api.notion.com/v1/pages"
592
+ payload = {
593
+ "properties": {
594
+ "title": {
595
+ "title": [{"text": {"content": title}}]
596
+ }
597
+ }
598
+ }
599
+ if parent_id:
600
+ clean_parent = parent_id.replace("-", "").strip()
601
+ payload["parent"] = {"page_id": clean_parent}
602
+ else:
603
+ search_res = await notion_search(user_id, max_results=1)
604
+ if "Page/DB ID: " in search_res:
605
+ p_id = search_res.split("Page/DB ID: ")[1].split(" ")[0].strip()
606
+ payload["parent"] = {"page_id": p_id.replace("-", "")}
607
+ else:
608
+ return "Cannot find parent page. Please provide a parent_id to create a new page in Notion."
609
+
610
+ if content:
611
+ payload["children"] = [
612
+ {
613
+ "object": "block",
614
+ "type": "paragraph",
615
+ "paragraph": {
616
+ "rich_text": [{"type": "text", "text": {"content": content}}]
617
+ }
618
+ }
619
+ ]
620
+
621
+ async with httpx.AsyncClient() as c:
622
+ res = await c.post(url, headers=headers, json=payload)
623
+ if res.status_code != 200:
624
+ return f"Error creating Notion page: {res.text}"
625
+ data = res.json()
626
+ new_id = data.get("id", "")
627
+ return f"Successfully created Notion page '{title}' (Page ID: {new_id})."
628
+ except Exception as e:
629
+ return f"Error creating Notion page: {str(e)}"
630
+
631
+ async def notion_append_block_children(user_id: str, page_id: str, content: str):
632
+ headers = await notion_get_headers(user_id)
633
+ if not headers:
634
+ return "Notion is not connected."
635
+ try:
636
+ clean_id = page_id.replace("-", "").strip()
637
+ url = f"https://api.notion.com/v1/blocks/{clean_id}/children"
638
+ payload = {
639
+ "children": [
640
+ {
641
+ "object": "block",
642
+ "type": "paragraph",
643
+ "paragraph": {
644
+ "rich_text": [{"type": "text", "text": {"content": content}}]
645
+ }
646
+ }
647
+ ]
648
+ }
649
+ async with httpx.AsyncClient() as c:
650
+ res = await c.patch(url, headers=headers, json=payload)
651
+ if res.status_code != 200:
652
+ return f"Error appending content to Notion: {res.text}"
653
+ return f"Successfully appended content to Notion page ID: {page_id}."
654
+ except Exception as e:
655
+ return f"Error appending content to Notion: {str(e)}"
656
+
657
  async def gmail_search_emails(user_id: str, query: str = "", max_results: int = 5):
658
  creds = await get_google_creds(user_id, "gmail")
659
  if not creds:
 
934
  return await drive_delete_file(user_id, args.get("file_id", ""))
935
  elif func_name == "drive_update_file":
936
  return await drive_update_file(user_id, args.get("file_id", ""), args.get("content", ""))
937
+ elif func_name == "notion_search":
938
+ return await notion_search(user_id, args.get("query", ""), args.get("max_results", 5))
939
+ elif func_name == "notion_get_page_content":
940
+ return await notion_get_page_content(user_id, args.get("page_id", ""))
941
+ elif func_name == "notion_create_page":
942
+ return await notion_create_page(user_id, args.get("parent_id"), args.get("title", "Untitled"), args.get("content", ""))
943
+ elif func_name == "notion_append_block_children":
944
+ return await notion_append_block_children(user_id, args.get("page_id", ""), args.get("content", ""))
945
  return "Unknown tool function."
946
 
947
  def count_tokens(messages):
 
1150
 
1151
  if content and not has_tool_call:
1152
  yield json.dumps({"answer": content}) + "\n"
1153
+ except json.JSONDecodeError:
1154
  continue
1155
 
1156
  if has_tool_call and tool_calls_acc: