# Project export: Smart Highlighter

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: CruzHacks 2024
- Tagline: Highlight Brilliance, Question with Ease: Unleash the Power of Smart Learning!
- Devpost: https://devpost.com/software/smart-highlighter
- GitHub: https://github.com/tqpatil/CruzHacks_2024_PGP
- Team: 4 GitHub contributor(s) — Aryaman Gautam (98 commits), manav072004 (19 commits), Tanishq Patil (11 commits), tqpatil (4 commits)

## Devpost submission (written by the team)

### Inspiration

We created Smart Highlighter because we wanted to enhance the productivity of reading and taking notes. When we are highlighting multi-page papers we may sometimes feel overwhelmed or confused by our notes. Smart Highlighter will allow us to interact with an AI that can provide us answers to the notes that we took. This can allow us to highlight notes as we read. We can later use this extension to gain a deeper understanding of the notes with AI.

### What it does

The product allows users to easily save highlighted text in an archive. Through the chat feature in the extension, users can interact with an AI to get answers related to their notes. This helps users quickly grasp the key points of their notes and enhances collaboration using AI for an improved learning experience.

### How we built it

To build our product, we decided to build a Chrome extension. This Chrome extension was built using JavaScript. The Chrome extension would allow users to utilize the extension whenever they were reading online material. To integrate artificial intelligence that would answer user questions related to the notes, we used Vectara. We created a backend application using Python, FAST API, and Vectara. We deployed our backend system using Heroku. We used our backend system link and fetched APIs from our Chrome Extension application to our backend Python program. The APIs would transfer questions and notes to the backend program. The Python program would put the data into the Vectara Model. The output of the AI model would then be transmitted back to our Chrome extension program where we displayed the output in a clean format through HTML, CSS, and JavaScript.

### Challenges we ran into

One of the biggest challenges we had to face was developing a connection between the backend side of the code that was programmed primarily in Python to the front end side that incorporated a mixture of HTML/CSS and JavaScript. One specific challenge was that we struggled to find a way to pack our data into a JSON file in our Python and unpack that file when we fetch the data from our JavaScript program. Our data would often be lost and we would receive undefined or null variables. However, we overcame this challenge by using debugging practices like console.log which helped us identify if our code was working accordingly and helped link our program. Furthermore, developing a Chrome extension was a new and difficult experience for us. To get familiar with the process and technology we looked at online tutorials and documentation to master it and ensure its functionality.

### Accomplishments we're proud of

Working as a collective team and integrating a relationship between backend and frontend engineering to eventually develop a working product is an accomplishment we are extremely proud of. Coming into CruzHacks as a full team of first-time hackers, we were unsure about our expectations and simply hoping to have a good time with people within an innovative space, but being able to bring something to the table was the icing on the cake of our hackathon experience.

### What we learned

The most valuable lesson we gained from our experience was collaborating as a team to develop a technology. Unlike our university courses that focus on individual programming projects, we had the opportunity to work together as a group to create something. Furthermore, we learned a lot and acquired new skills from working with new technologies like Vectara and Chrome extensions.

### What's next

We look to create a database for storage of notes to allow more users to use our product at once. By adding this feature, we can also allow people to separate their notes into several categories for recall. We also hope to implement a conversational model as a middle-man between Vectara and our users, making the chat experience much more comprehensive. Finally, we want to be able to organize your notes for you, which would also be built via a conversational model.

## README (from the GitHub repository)

# Smart Highlighter
<img width="267" alt="Screenshot 2024-01-21 001227" src="https://github.com/tqpatil/CruzHacks_2024_PGP/assets/34226808/911d6f25-f95a-461e-99ed-8619d8a9b96a">

``` A bright and simple way to take notes and reflect on them. ``` 
## Key Features:
- We allow you to easily store and recall notes by first:
1. Highlighting the sentence or block of text you want to store with your cursor
<img width="300" alt="Screenshot 2024-01-21 005158" src="https://github.com/tqpatil/CruzHacks_2024_PGP/assets/34226808/28869ade-0a19-4675-bad1-221be6c7368e">
  
2. Right clicking and selecting our "Store in Archive" option in the dropdown menu for each of your notes
<img width="367" alt="Screenshot 2024-01-21 001406" src="https://github.com/tqpatil/CruzHacks_2024_PGP/assets/34226808/62c1de7c-772f-435c-a5fa-257e1606ca66">

3. Clicking on our extension icon via the Chrome Extensions tab
<img width="247" alt="Screenshot 2024-01-21 005347" src="https://github.com/tqpatil/CruzHacks_2024_PGP/assets/34226808/f2c6a099-7a28-4a4d-aaea-9e9cf3b2970d">

4. Chat with your notes
<img width="263" alt="image" src="https://github.com/tqpatil/CruzHacks_2024_PGP/assets/34226808/37ffbb3a-879a-4f78-acc0-27496cb55e5b">

## Setup
1. Clone this repository
2. Navigate to chrome://extensions/ within Google Chrome
3. Enable Developer Mode
<img width="131" alt="image" src="https://github.com/tqpatil/CruzHacks_2024_PGP/assets/34226808/0ec681b8-ccd5-4e37-b58b-a5e22f802c74">

4. Select "Load Unpacked" in the top bar
5. Navigate to and select the "extension" folder
### You're ready to go!


## Detected evidence (automated analysis)

Indexed codebase: 10 recognized source files, 17 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- PostgreSQL (technology) — detected in the code
- Python (language) — detected in the code

## Codebase structure (from repository index)

### Files (12 of 12)

```
extension/background.js
extension/chat.html
extension/chat.js
extension/content.js
extension/manifest.json
extension/popup.css
extension/popup.html
extension/popup.js
llm-agent/environment/endpoint.py
llm-agent/environment/requirements.txt
llm-agent/environment/testFile.py
README.md
```

### Dependencies

- llm-agent/environment/requirements.txt: click@==8.1.3, colorama@==0.4.6, et-xmlfile@==1.1.0, flatbuffers@==23.3.3, greenlet@==2.0.2, itsdangerous@==2.1.2, Jinja2@==3.1.2, keras@==2.12.0, libclang@==16.0.0, MarkupSafe@==2.1.2, mpmath@==1.3.0, numpy@==1.23.5, oauthlib@==3.2.2, opencv-python@==4.7.0.72, openpyxl@==3.1.2, packaging@==23.1, pandas@==2.0.2, protobuf@==4.22.3, psycopg2@==2.9.6, pyasn1@==0.5.0, pygame@==2.5.2, python-dateutil@==2.8.2, pytz@==2023.3, rsa@==4.9, scipy@==1.11.4, six@==1.16.0, SQLAlchemy@==2.0.17, sympy@==1.12, tensorboard-data-server@==0.7.0, tensorflow-estimator@==2.12.0, tensorflow-io-gcs-filesystem@==0.31.0, termcolor@==2.3.0, thread6@==0.2.0, threadpoolctl@==3.2.0, typing_extensions@==4.5.0, tzdata@==2023.3, urllib3@==1.26.15, Werkzeug@==2.2.3, wrapt@==1.14.1

### Recent commits (newest first)

- Update README.md
- bolding
- Merge branch 'main' of https://github.com/tqpatil/CruzHacks_2024_PGP
- Final Extension
- Update README.md
- Merge branch 'main' of https://github.com/tqpatil/CruzHacks_2024_PGP
- test
- Update README.md
- Update README.md
- changed name
- test
- test
- margins
- reduce margin
- test reduce button size
- Final Extension
- changed font color
- updating background
- Merge branch 'main' of https://github.com/tqpatil/CruzHacks_2024_PGP
- test

## Key source files (fetched from GitHub, selected and truncated for size)

### llm-agent/environment/requirements.txt

```
click==8.1.3
colorama==0.4.6
et-xmlfile==1.1.0
flatbuffers==23.3.3
greenlet==2.0.2
itsdangerous==2.1.2
Jinja2==3.1.2
keras==2.12.0
libclang==16.0.0
MarkupSafe==2.1.2
mpmath==1.3.0
numpy==1.23.5
oauthlib==3.2.2
opencv-python==4.7.0.72
openpyxl==3.1.2
packaging==23.1
pandas==2.0.2
protobuf==4.22.3
psycopg2==2.9.6
pyasn1==0.5.0
pygame==2.5.2
python-dateutil==2.8.2
pytz==2023.3
rsa==4.9
scipy==1.11.4
six==1.16.0
SQLAlchemy==2.0.17
sympy==1.12
tensorboard-data-server==0.7.0
tensorflow-estimator==2.12.0
tensorflow-io-gcs-filesystem==0.31.0
termcolor==2.3.0
thread6==0.2.0
threadpoolctl==3.2.0
typing_extensions==4.5.0
tzdata==2023.3
urllib3==1.26.15
Werkzeug==2.2.3
wrapt==1.14.1

```

### extension/popup.css

```css
body {
    max-width: 400px;
    margin: 20px;
  }
  
  .chat-container {
    display: none;
    margin-top: 20px;
  }
  
```

### extension/chat.js

```javascript
document.getElementById("send-question").addEventListener("click", () => {
    const question = document.getElementById("chat-input").value;
    document.getElementById("chat-input").value = "";
  
    chrome.runtime.sendMessage({ action: "sendQuestion", text: question });
  });
  
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.action === "displayVectaraOutput") {
      const chatOutput = document.getElementById("chat-output");
      chatOutput.innerHTML += `<p>Vectara: ${request.output}<div><p>${request.source}</p></div></p>`;
      //chatOutput.innerHTML += `<p>Source: ${request.source}</p>`;
    }
  });
  
```

### extension/content.js

```javascript
function handleSaveHighlight() {
  const highlightedText = window.getSelection().toString();
  chrome.runtime.sendMessage({ action: "saveHighlight", text: highlightedText });
}

const button = document.createElement("button");
button.textContent = "Save Highlight";
button.addEventListener("click", handleSaveHighlight);
document.body.appendChild(button);


chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "openPopup") {
    chrome.runtime.openOptionsPage();
  } else if (request.action === "getHighlight") {
    sendResponse({ text: window.getSelection().toString() });
  } else if (request.action === "retrieveBackendData") {
    // Implement the logic to retrieve data from your backend
    // Send a message to the background script with the retrieved data
    chrome.runtime.sendMessage({ action: "retrieveBackendData" });
  }
});

```

### extension/chat.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <div id="logo-title" style="display: flex; flex-direction: column; align-items: center; justify-content: center;">
    <img src="images/logoUI.png" alt="Logo">
    <h1>Smart Highlighter</h1>
  </div>
  <title>Chat with Vectara</title>
  <script nonce="random_string_here" src="chat.js"></script>
  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 0;
      padding: 0;
      background-color: #f0f0f0;
    }
    #chat-container {
      max-width: 600px;
      margin: auto;
      background-color: white;
      border-radius: 5px;
      box-shadow: 0px 0px 10px rgba(0,0,0,0.1);
      padding:20px; 
    }
    #send-question {
        background-color:#4CAF50; 
        color:white; 
        border:none; 
        padding:10px; 
        text-align:center; 
        display:inline-block; 
        font-size:16px; 
        margin-top :10px ;  
     }
     h1{
         color:#333 ;
     }
   </style>
</head>
<body>
<div id="chat-container">
<h1>Chat with Vectara</h1>
<div id="chat-output"></div>
<label for="chat-input">Enter your question:</label><br/>
<input type="text" id="chat-input" title="Type your question" placeholder="Type your question here..." style = "width :100% ; padding :10 px ; margin-top :5 px "><br/>
<button id ="send-question">Send Question</button>
</div>
</body>
</html>


```

### extension/popup.js

```javascript
const highlightsList = document.getElementById("highlights-list");
const chatContainer = document.querySelector(".chat-container");
const chatOutput = document.getElementById("chat-output");


chrome.runtime.onInstalled.addListener(function() {
  chrome.contextMenus.create({
    id: "store",
    title: "Store Highlight",
    contexts: ["all"]
  });
});

chrome.contextMenus.onClicked.addListener(async function (info, tab) {
  if (info.menuItemId === "store") {
    let result;
    try {
      [{ result }] = await chrome.scripting.executeScript({
        target: { tabId: tab.id },
        func: () => getSelection().toString(),
      });
    } catch (e) {
      return; // ignoring an unsupported page like chrome://extensions
    }
    chrome.runtime.sendMessage({ action: "saveHighlight", text: result });
  }
});

document.getElementById("send-to-backend").onclick = async () => {
  const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
  let result;
  try {
    [{result}] = await chrome.scripting.executeScript({
      target: {tabId: tab.id},
      func: () => getSelection().toString(),
    });
  } catch (e) {
    return; // ignoring an unsupported page like chrome://extensions
  }
  chrome.runtime.sendMessage({ action: "saveHighlight", text: result });
};


document.getElementById("chat-input").addEventListener("keypress", (event) => {
  if (event.key === "Enter") {
    const question = event.target.value;
    event.target.value = "";

    chrome.runtime.sendMessage({ action: "sendQuestion", text: question });
  }
});

//document.getElementById('open-chat').addEventListener('click', function() {
//  window.open('chat.html', '_blank', 'noopener');
//});

chatOutput.style.fontSize = "16px";
chatOutput.style.color = "white";
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "displayVectaraOutput") {
    chatOutput.innerHTML += `<p>Question: ${request.question}</p>`;
    chatOutput.innerHTML += `<p><strong>Smart Highlighter:</strong> ${request.output}</p>`;
    chatOutput.innerHTML += `<p>Source: ${request.source}</p>`;
  }
});



```

### extension/popup.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Save Highlight</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
  <style>
    body {
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 20px;
        background-color: #3b3b3b;
    }
    label {
      display: block;
      width: 100%;
      height: 100%;
      font-size: 16px;
      box-sizing: border-box;
    }
    #logo-title {
        display: flex;
        align-items: center;
        background-color: #3b3b3b;
        padding: 10px;
        border-radius: 5px;
    }
    #logo-title img {
        width:50px; 
        height:auto; 
        margin-right:18px; 
        border-radius :50%;
    }
    h1, h2 {
        color:#ffffff; 
    }
    #chat-input {
        font-size: 12px;
        margin-top: 15px;
        padding: 5px;
        color: #000000;
    }
    button {
        background-color:#4CAF50;
        border:none; 
        color:rgb(0, 0, 0); 
        padding :8px 16px ; 
        text-align:center ; 
        text-decoration:none ; display:inline-block ;
        font-size :12px ;
        margin :4px 2px ;
        transition-duration :0.4s ;
        cursor:pointer ;
     }
    button:hover{
        background-color:white ; color:black ; border :2mm ridge #4CAF50
    }
    ul{
        list-style-type:none ;
        padding-inline-start :20 px
    }    
   </style>
</head>
<body>
<div id="logo-title">
   <img src="images/logoUI.png" alt="Logo">
   <h1>Smart Highlighter</h1>
</div>
<hr style="border-top: 1px solid lightgrey;">
<h2 style="--font-size:22px;">Save Highlights</h2>
<ul id="highlights-list"></ul>
<button id="send-to-backend">Store to Archive</button><br><br>
<hr style="border-top: 1px solid lightgrey;">
<h2 style="--font-size:22px;">Chat with Smart Highlight</h2>
<div style = "background-color:#3b3b3b ;padding :10 px;border-radius :5 px;">
<input type="text" style="width: 300px; height: 40px;" id="chat-input" title="Type your message" placeholder="Type your message here...">
</div>
<div id="chat-output"></div>
<!--<button id="open-chat"><a href="chat.html" target="_blank">Open Chat</a></button>-->
<script src="popup.js"></script>
</body>
</html>
```

### extension/background.js

```javascript
// background.js
chrome.action.onClicked.addListener(() => {
  chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
    chrome.tabs.sendMessage(tabs[0].id, "openPopup");
  });
});

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "saveHighlight") {
    const highlightedText = request.text;
    if (highlightedText == " " || highlightedText == ""){
      console.log("error");
    }
    console.log(highlightedText);
    fetch("https://smart-highlighter-ffd082798250.herokuapp.com/store", {
      method: "POST",
      body: JSON.stringify({ "text": highlightedText, "secret": "Tanishqisthegoat" })
    })
    .then(response => response.json())
    .then(data => {
      console.log("Backend response:", data);
    });
    setTimeout(() => {fetch("https://smart-highlighter-ffd082798250.herokuapp.com/resetCorp", {
      method: "Put",
      body: JSON.stringify({ "secret": "Tanishqisthegoat", "corpus_id": 4})
    }).then(data => {
      console.log("Backend response:", data);
    })
    }, 10000);

  } else if (request.action === "sendQuestion") {
    fetch("https://smart-highlighter-ffd082798250.herokuapp.com/replace", {
      method: "Put",
      body: JSON.stringify({ "secret": "Tanishqisthegoat", "corpus_id": 4})
    }).then(data => {
      console.log("Backend response:", data);
    });
    console.log(request.text);
    fetch("https://smart-highlighter-ffd082798250.herokuapp.com/readReq", {
      method: "POST",
      body: JSON.stringify({ "text" : request.text, "secret": "Tanishqisthegoat", "corpus_id": 4 })
    }).then(response => response.json())
    .then(parsedData => {
      console.log(parsedData.text);
      chrome.runtime.sendMessage({ action: "displayVectaraOutput", output: parsedData.text, source: parsedData.source, question: request.text});
    })
    .catch(error => {
      console.error("Error:", error);
    });


  } else if (request.action === "openChatPage") {
    chrome.tabs.create({ url: "chat.html" });
  } else if (request.action === "retrieveBackendData") {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      chrome.tabs.sendMessage(tabs[0].id, { action: "retrieveBackendData" });
    });
  }

});

RunStore = function(word){
  console.log(word.selectionText);
  const highlightedText = word.selectionText;
  if (highlightedText == " " || highlightedText == ""){
    console.log("error");
  }
  console.log(highlightedText);
  fetch("https://smart-highlighter-ffd082798250.herokuapp.com/store", {
    method: "POST",
    body: JSON.stringify({ "text": highlightedText, "secret": "Tanishqisthegoat" })
  })
  .then(response => response.json())
  .then(data => {
    console.log("Backend response:", data);
  });
  setTimeout(() => {fetch("https://smart-highlighter-ffd082798250.herokuapp.com/resetCorp", {
    method: "Put",
    body: JSON.stringify({ "secret": "Tanishqisthegoat", "corpus_id": 4})
  }).then(data => {
    console.log("Backend response:", data);
  })
  }, 10000);
}
chrome.contextMenus.removeAll(function() {
  chrome.contextMenus.create({
   id: "1",
   title: "Store in Archive",
   contexts:["selection"],  // ContextType
  }); })

chrome.contextMenus.onClicked.addListener(RunStore);
```

### llm-agent/environment/testFile.py

```python
import requests
import json
r = requests.post("http://127.0.0.1:8000/replace", data= json.dumps({"secret": "Tanishqisthegoat", "corpus_id":4})).json()
print(r)
```

### llm-agent/environment/endpoint.py

```python
# from sentence_transformers import SentenceTransformer
# import uuid
# from langchain.tools import StructuredTool
# from langchain.prompts import MessagesPlaceholder
# from langchain.agents import AgentType, initialize_agent, Tool
# from langchain.chat_models import ChatOpenAI
# from langchain.memory import ConversationBufferMemory
# from pydantic import BaseModel, Field
# import spacy
from fastapi import FastAPI, Request, Response
from fastapi.encoders import jsonable_encoder
from vectara import vectara
import os
app = FastAPI()
secretKey = "Tanishqisthegoat"
def read_env_file(file_path):
    env_vars = {}
    with open(file_path, "r") as file:
        for line in file:
            # Ignore comments and empty lines
            if line.startswith("#") or line.strip() == "":
                continue
            # Split the line by the first "=" sign
            key, value = line.split("=", 1)
            # Remove whitespace and quotes
            key = key.strip()
            value = value.strip().strip("\"'")
            # Add the key-value pair to the dictionary
            env_vars[key] = value
    return env_vars
def readReq(text, corpus_id, secret):
    if(secret != secretKey):
        return {'ErrorCode': 404, 'Response' : "Invalid secret key"}
    vars = read_env_file(".env")
    client = vectara(vars["VECTARA_CUSTOMER_ID"], vars["VECTARA_CLIENT_ID"], vars["VECTARA_CLIENT_SECRET"])
    response= client.query(corpus_id,text, top_k=6)
    return response
def reset(corpus_id, secret):
    if(secret != secretKey):
        return {'ErrorCode': 404, 'Response' : "Invalid secret key"}
    vars = read_env_file(".env")
    client = vectara(vars["VECTARA_CUSTOMER_ID"], vars["VECTARA_CLIENT_ID"], vars["VECTARA_CLIENT_SECRET"])
    client.reset_corpus(corpus_id)
def replace(corpus_id, secret):
    if(secret != secretKey):
        return {'ErrorCode': 404, 'Response' : "Invalid secret key"}
    vars = read_env_file(".env")
    client = vectara(vars["VECTARA_CUSTOMER_ID"], vars["VECTARA_CLIENT_ID"], vars["VECTARA_CLIENT_SECRET"])
    client.upload(corpus_id, 'temp.txt', description= "Knowledge base")
def store(text,secret):
    if(secret != secretKey):
        return {'ErrorCode': 404, 'Response' : "Invalid secret key"}
    f = open("temp.txt", "a")
    f.write(text)
    f.close()
@app.get("/")
async def home():
    return "What are you doing here"
@app.post("/readReq")
async def request_api(request: Request, response: Response):
    data = await request.json()
    text = data.get("text")
    secret = data.get("secret")
    corpus_id = data.get("corpus_id")
    result = readReq(text, corpus_id, secret)
    try:
        #if(result["responseSet"][0]["summary"][0]["status"][0]["code"] == 'QRY__SMRY__NO_QUERY_RESULTS'):
        result = jsonable_encoder({result['responseSet'][0]['summary'][0]["status"][0]["statusDetail"]: "None"})
    except Exception:
        result = jsonable_encoder({result['responseSet'][0]['summary'][0]["text"]: result['responseSet'][0]['response'][0]['text']})
    response.status_code = 200
    response.headers["Content-Type"] = "application/json"
    return result
@app.put("/replace")
async def replace_api(request: Request, response: Response):
    # Get the request data as JSON
    data = await request.json()
    # Get the corpus_id from the data
    corpus_id = data.get("corpus_id")
    secret = data.get("secret")
    # Call the replace function
    replace(corpus_id, secret)
    # Set the response status code and content type
    response.status_code = 200
    response.headers["Content-Type"] = "application/json"
    # Return a success message as JSON
    return {"message": "Corpus replaced successfully"}

# Create a path operation for the store function
@app.post("/store")
async def store_api(request: Request, response: Response):
    # Get the request data as JSON
    data = await request.json()
    # Get the text from the data
    text = data.get("text")
    secret = data.get("secret")
    # Call the store function
    store(text,secret)
    # Set the response status code and content type
    response.status_code = 200
    response.headers["Content-Type"] = "application/json"
    # Return a success message as JSON
    return {"message": "Text stored successfully"}
@app.put("/resetCorp")
async def reset_api(request:Request, response:Response):
    data = await request.json()
    secret = data.get("secret")
    corpus_id = data.get("corpus_id")
    reset(corpus_id, secret)
    response.status_code = 200
    response.headers["Content-Type"] = "application/json"
    return 
##sentence bert dimension is 384

# if __name__ == "__main__":
#     dic = request("How much water is in Africa?", 4)
#     print(dic['responseSet'][0]['summary'][0]["text"])
#     print("source:" + dic['responseSet'][0]['response'][0]['text'])
    ##corpus_id = 4
```