mayank1101 commited on
Commit
28d8100
·
verified ·
1 Parent(s): 3180e31

Upload 7 files

Browse files
Files changed (7) hide show
  1. gradio_app.py +180 -0
  2. main.py +82 -0
  3. model_registry.py +26 -0
  4. requirements.txt +18 -0
  5. utils.py +49 -0
  6. websearch.py +99 -0
  7. websites.yaml +135 -0
gradio_app.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import gradio as gr
2
+ # import httpx
3
+ # import json
4
+ # from typing import Tuple, Any
5
+
6
+ # # Define the FastAPI endpoint URL
7
+ # FASTAPI_ENDPOINT = "http://localhost:8000/websearch"
8
+
9
+
10
+ # def query_api(query: str) -> Tuple[Any, Any]:
11
+ # try:
12
+ # # Send POST request to FastAPI endpoint with streaming enabled
13
+ # with httpx.Client() as client:
14
+ # with client.stream("POST", FASTAPI_ENDPOINT, json={"query": query}, timeout=60.0) as response:
15
+ # response.raise_for_status() # Raise an exception for 4xx or 5xx status codes
16
+
17
+ # # Process the streaming response
18
+ # response_data = ""
19
+ # for chunk in response.iter_text():
20
+ # response_data += chunk
21
+
22
+ # # Parse the accumulated response data as JSON
23
+ # response_json = json.loads(response_data)
24
+
25
+ # # Extract content and citations from the response JSON
26
+ # content = response_json.get("content", "")
27
+ # citations = response_json.get("citations", [])
28
+
29
+ # # Beautify content using Markdown formatting
30
+ # beautified_content = f"# Search Results\n\n{content}"
31
+
32
+ # # Beautify citations by adding Markdown links
33
+ # beautified_citations = "# Citations\n\n"
34
+ # for i, citation in enumerate(citations, start=1):
35
+ # beautified_citations += f"{i}. [{citation}]({citation})\n"
36
+
37
+ # # Yield the beautified content and citations
38
+ # yield beautified_content, beautified_citations
39
+ # except httpx.TimeoutException:
40
+ # yield "Request timed out. Please try again later.", ""
41
+ # except httpx.HTTPStatusError as e:
42
+ # yield f"HTTP error occurred: {e}", ""
43
+ # except Exception as e:
44
+ # yield f"An error occurred: {e}", ""
45
+
46
+
47
+ # # Create Gradio interface
48
+ # with gr.Blocks(css=".gradio-container { background-color: #f5f5f5; padding: 20px; border-radius: 10px; }") as demo:
49
+ # gr.Markdown("# Web Search Application")
50
+
51
+ # with gr.Row():
52
+ # with gr.Column():
53
+ # query = gr.Textbox(
54
+ # label="Enter your query",
55
+ # placeholder="Type your search query here...",
56
+ # lines=2,
57
+ # max_lines=4,
58
+ # value="",
59
+ # elem_id="query-input"
60
+ # )
61
+ # submit_button = gr.Button("Search")
62
+
63
+ # with gr.Column():
64
+ # output_content = gr.Textbox(
65
+ # label="Response Content",
66
+ # placeholder="Search results will appear here...",
67
+ # lines=10,
68
+ # max_lines=20,
69
+ # value="",
70
+ # elem_id="response-content"
71
+ # )
72
+ # output_citations = gr.Textbox(
73
+ # label="Citations",
74
+ # placeholder="Citations will appear here...",
75
+ # lines=5,
76
+ # max_lines=10,
77
+ # value="",
78
+ # elem_id="response-citations"
79
+ # )
80
+
81
+ # # Set up event listener
82
+ # submit_button.click(query_api, inputs=query, outputs=[output_content, output_citations])
83
+
84
+ # gr.Markdown("Powered by FastAPI and Gradio")
85
+
86
+ # # Launch the Gradio application
87
+ # demo.launch()
88
+
89
+
90
+ import gradio as gr
91
+ import httpx
92
+ import json
93
+
94
+ # Define the FastAPI endpoint URL
95
+ FASTAPI_ENDPOINT = "http://localhost:8000/websearch"
96
+
97
+ def query_api(query: str) -> tuple:
98
+ try:
99
+ # Send POST request to FastAPI endpoint with streaming enabled
100
+ with httpx.Client() as client:
101
+ with client.stream("POST", FASTAPI_ENDPOINT, json={"query": query}, timeout=60.0) as response:
102
+ response.raise_for_status() # Raise an exception for 4xx or 5xx status codes
103
+
104
+ # Process the streaming response
105
+ response_data = ""
106
+ for chunk in response.iter_text():
107
+ response_data += chunk
108
+
109
+ # Parse the accumulated response data as JSON
110
+ response_json = json.loads(response_data)
111
+
112
+ # Extract content and citations from the response JSON
113
+ content = response_json.get("content", "")
114
+ citations = response_json.get("citations", [])
115
+
116
+ # Beautify content using Markdown formatting
117
+ beautified_content = f"# Search Results\n\n{content}"
118
+
119
+ # Beautify citations by adding Markdown links
120
+ beautified_citations = "# Citations/Sources\n\n"
121
+ for i, citation in enumerate(citations, start=1):
122
+ beautified_citations += f"{i}. [{citation}]({citation})\n"
123
+
124
+ # Yield the beautified content and citations
125
+ yield beautified_content, beautified_citations
126
+ except httpx.TimeoutException:
127
+ yield "# Request Timeout\n\nRequest timed out. Please try again later.", ""
128
+ except httpx.HTTPStatusError as e:
129
+ yield f"# HTTP Error\n\nHTTP error occurred: {e}", ""
130
+ except Exception as e:
131
+ yield f"# Error\n\nAn error occurred: {e}", ""
132
+
133
+ # Create Gradio interface
134
+ with gr.Blocks(css=".gradio-container { background-color: #f5f5f5; padding: 20px; border-radius: 10px; }", theme=gr.themes.Citrus()) as demo:
135
+ gr.Markdown("# Web Search Application")
136
+
137
+ with gr.Row():
138
+ with gr.Column(
139
+ render=True,
140
+ show_progress=True
141
+ ):
142
+ query = gr.Textbox(
143
+ label="Enter your query",
144
+ placeholder="Type your search query here...",
145
+ lines=2,
146
+ max_lines=4,
147
+ value="",
148
+ elem_id="query-input"
149
+ )
150
+ submit_button = gr.Button("Search")
151
+
152
+ with gr.Column(
153
+ render=True,
154
+ show_progress=True
155
+ ):
156
+ output_content = gr.Markdown(
157
+ label="Response Content",
158
+ value="",
159
+ elem_id="response-content",
160
+ height="600px",
161
+ visible=True,
162
+ show_label=True
163
+
164
+ )
165
+ output_citations = gr.Markdown(
166
+ label="Citations",
167
+ value="",
168
+ elem_id="response-citations",
169
+ height="200px",
170
+ visible=True,
171
+ show_label=True
172
+ )
173
+
174
+ # Set up event listener
175
+ submit_button.click(query_api, inputs=query, outputs=[output_content, output_citations])
176
+
177
+ gr.Markdown("Powered by FastAPI and Gradio")
178
+
179
+ # Launch the Gradio application
180
+ demo.launch()
main.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import asyncio
4
+ from typing import AsyncGenerator
5
+
6
+ from fastapi.middleware.cors import CORSMiddleware
7
+ from fastapi.responses import StreamingResponse
8
+ from fastapi import FastAPI, HTTPException
9
+
10
+ from websearch import QueryRequest, PerplexityClient, parse_perplexity_response
11
+
12
+ # load .env file
13
+ from dotenv import load_dotenv
14
+ load_dotenv()
15
+
16
+ # Initialize FastAPI app
17
+ app = FastAPI()
18
+
19
+ # Add CORS middleware to allow frontend connections
20
+ app.add_middleware(
21
+ CORSMiddleware,
22
+ allow_origins=["*"], # Adjust this in production
23
+ allow_credentials=True,
24
+ allow_methods=["*"],
25
+ allow_headers=["*"],
26
+ )
27
+
28
+ # Initialize Perplexity client
29
+ perplexity_client = PerplexityClient(
30
+ api_key=os.environ["PERPLEXITY_AUTH_TOKEN"]
31
+ )
32
+
33
+
34
+ async def generate_stream(query: str) -> AsyncGenerator[str, None]:
35
+ """
36
+ Async generator to stream JSON response
37
+
38
+ Args:
39
+ query (str): User query
40
+
41
+ Yields:
42
+ str: JSON-encoded chunks of response
43
+ """
44
+ try:
45
+ # Fetch response from Perplexity
46
+ response = await perplexity_client.generate_response(query)
47
+
48
+ # Parse the response
49
+ parsed_response = parse_perplexity_response(response)
50
+
51
+ # Stream the parsed response as JSON chunks
52
+ yield json.dumps(parsed_response)
53
+
54
+ except Exception as e:
55
+ yield json.dumps({"error": str(e)})
56
+
57
+
58
+ @app.post("/websearch")
59
+ async def handle_query(request: QueryRequest):
60
+ """
61
+ Endpoint to handle user queries and stream responses
62
+
63
+ Args:
64
+ request (QueryRequest): Query request model
65
+
66
+ Returns:
67
+ StreamingResponse: Streaming JSON response
68
+ """
69
+ return StreamingResponse(
70
+ generate_stream(request.query),
71
+ media_type="application/json"
72
+ )
73
+
74
+
75
+ # Optional: Health check endpoint
76
+ @app.get("/health")
77
+ async def health_check():
78
+ return {"status": "healthy"}
79
+
80
+ # Run the app with:
81
+ # uvicorn main:app --reload
82
+ # Make sure to set PERPLEXITY_API_KEY environment variable
model_registry.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from utils import get_current_date, get_websites
2
+
3
+ current_date = get_current_date() # returns current date
4
+ websites = get_websites() # returns list of websites
5
+
6
+ perplexity_prompt = f"""
7
+ You are an advanced information retrieval assistant. Your primary task is to provide accurate and concise answers to user queries. You must refer to the following LIST OF WEBSITES to retrieve the context required to answer the user query.
8
+
9
+ LIST OF WEBSITES TO REFER - {', '.join(websites)}
10
+
11
+
12
+ IMPORTANT NOTE - You must always include citations source URLs in the response for user transparency. Provide information that is relevant and up-to-date as of this {current_date} date.
13
+ """
14
+
15
+
16
+ model_card = {
17
+ "perplexity": {
18
+ "model": "llama-3.1-sonar-large-128k-online",
19
+ "url": "https://api.perplexity.ai/chat/completions",
20
+ "inference_config": {
21
+ "max_tokens": 4094,
22
+ "temperature": 0
23
+ },
24
+ "prompt": perplexity_prompt
25
+ }
26
+ }
requirements.txt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ python-dotenv==1.0.1
2
+ langchain==0.3.7
3
+ langchain-community==0.3.7
4
+ langchain-aws==0.2.7
5
+ tavily-python==0.5.0
6
+ langchain-qdrant==0.2.0
7
+ langchain-openai==0.2.9
8
+ langchain-ollama==0.2.0
9
+ # langchain-mistralai==0.2.2
10
+ # langchain-huggingface==0.1.2
11
+ faiss-cpu==1.9.0.post1
12
+ rapidocr-onnxruntime==1.4.0
13
+ fastapi==0.115.5
14
+ httpx==0.28.0
15
+ uvicorn==0.32.1
16
+ pydantic==2.10.2
17
+ aiofiles==23.2.1 # for gradio 24.1.0 for unstructured
18
+ gradio==5.8.0
utils.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import yaml
3
+ import datetime
4
+ from typing import Dict, List
5
+
6
+
7
+ def get_current_date() -> str:
8
+ """returns present date.
9
+
10
+ Returns:
11
+ str: return present date as a string.
12
+ """
13
+ current_date = datetime.date.today().strftime("%Y-%m-%d") # provide current date to LLM context
14
+ return current_date
15
+
16
+
17
+ def read_yaml(file_path: str) -> Dict:
18
+ """_summary_
19
+
20
+ Args:
21
+ file_path (str): wesites.yaml file path
22
+
23
+ Raises:
24
+ ValueError: raise error is file_path is empty or if websites.yaml file is missing.
25
+
26
+ Returns:
27
+ Dict: return List websites to be used for websearch.
28
+ """
29
+ websites_yaml = None
30
+ if not read_yaml:
31
+ raise ValueError("Website yaml config file missing")
32
+ return websites_yaml
33
+ else:
34
+ with open(file_path, 'r') as file:
35
+ websites_yaml = yaml.load(file, Loader=yaml.SafeLoader) # reads .yaml file
36
+ return websites_yaml
37
+
38
+
39
+ def get_websites() -> List[str]:
40
+ """reads websites.yaml file and return list of webistes
41
+
42
+ Returns:
43
+ List[str]: List of websites
44
+ """
45
+ file_path = os.path.join(os.getcwd(), 'websites.yaml') # get websites.yaml file path
46
+ websites = read_yaml(file_path) # read wesbites.yaml file
47
+ if not websites:
48
+ return []
49
+ return websites['public_websites'] # return list of public files
websearch.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ from typing import Dict, Any
3
+
4
+ import httpx
5
+ from pydantic import BaseModel
6
+
7
+ from model_registry import model_card
8
+
9
+ perpelxity_card = model_card["perplexity"]
10
+
11
+
12
+ class QueryRequest(BaseModel):
13
+ query: str
14
+
15
+
16
+ class PerplexityClient:
17
+ def __init__(self, api_key: str):
18
+ self.api_key = api_key
19
+ self.base_url = perpelxity_card["url"]
20
+ self.headers = {
21
+ "Authorization": f"Bearer {api_key}",
22
+ "Content-Type": "application/json"
23
+ }
24
+
25
+ async def generate_response(self, query: str) -> Dict[str, Any]:
26
+ payload = {
27
+ "model": perpelxity_card["model"],
28
+ "messages": [
29
+ {
30
+ "role": "system",
31
+ "content": perpelxity_card["prompt"]
32
+ },
33
+ {
34
+ "role": "user",
35
+ "content": query
36
+ }
37
+ ],
38
+ "max_tokens": perpelxity_card["inference_config"]["max_tokens"],
39
+ "temperature": perpelxity_card["inference_config"]["temperature"],
40
+ "stream": False, # We'll handle streaming separately
41
+ }
42
+
43
+ async with httpx.AsyncClient() as client:
44
+ try:
45
+ async with httpx.AsyncClient(
46
+ timeout=httpx.Timeout(
47
+ connect=10.0, # Connection timeout
48
+ read=45.0, # Read timeout
49
+ write=10.0, # Write timeout
50
+ pool=10.0 # Connection pool timeout
51
+ )
52
+ ) as client:
53
+ response = await client.post(
54
+ self.base_url,
55
+ headers=self.headers,
56
+ json=payload
57
+ )
58
+ response.raise_for_status()
59
+ return response.json()
60
+ except httpx.HTTPStatusError as e:
61
+ print(f"HTTP error occurred: {e}")
62
+ print(f"Response text: {e.response.text}")
63
+ raise
64
+ except httpx.RequestError as e:
65
+ print(f"Request error occurred: {e}")
66
+ raise
67
+ except Exception as e:
68
+ print(f"An unexpected error occurred: {e}")
69
+ raise
70
+
71
+
72
+ def parse_perplexity_response(response: Dict[str, Any]) -> Dict[str, Any]:
73
+ """
74
+ Parse the Perplexity API response and extract key information.
75
+
76
+ Args:
77
+ response (Dict[str, Any]): Raw response from Perplexity API
78
+
79
+ Returns:
80
+ Dict[str, Any]: Parsed response with content and citations
81
+ """
82
+ print("parse_perplexity_response called...")
83
+ # Basic citation extraction (this is a simple implementation)
84
+ # In a real-world scenario, you might want a more sophisticated citation extraction
85
+ citations = []
86
+
87
+ # default content to stream if no response from the Perplexity AI
88
+ default_content = (
89
+ "I'm sorry, I couldn't find any relevant information for your query from the available sources. "
90
+ "If you'd like, you can try rephrasing your question or provide more context to help refine the search. "
91
+ "Alternatively, let me know if you'd like assistance in a different area."
92
+ )
93
+
94
+ citations = response.get("citations", [])
95
+ content = response.get("choices", default_content)[0]["message"]["content"]
96
+ return {
97
+ "content": content,
98
+ "citations": citations
99
+ }
websites.yaml ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ public_websites:
2
+ - https://www.alimarket.es/base_de_datos,
3
+ - https://www.bain.cn/,
4
+ - http://zhgry.aiijournal.com/CN/1671-4393/home.shtml,
5
+ - https://moloprom.ru/,
6
+ - https://2023.dairyunion.ru/,
7
+ - https://www2.deloitte.com/cn/zh.html,
8
+ - https://www.emeoutlookmag.com/food-beverage",
9
+ - https://fabnews.live/,
10
+ - https://fmcgmagazine.co.uk/,
11
+ - https://www.foodbusinessnews.net/,
12
+ - https://www.fooddive.com/,
13
+ - https://www.foodengineeringmag.com/articles/96940-new-advances-make-aseptic-packaging-more-popular,
14
+ - https://foodindustryexecutive.com/
15
+ - http://spgykj.com/
16
+ - https://www.foodmanufacturing.com/
17
+ - https://www.foodprocessing.com/
18
+ - https://www.foodprocessing.com/
19
+ - https://www.foodsafetyafrica.net/
20
+ - https://www.foodtechbiz.com/packaging/tetra-pak-introduces-tetra-stelo-aseptic-package-with-minute-maid-juice-range-of-coca-cola-in-india
21
+ - https://www.fruit-processing.com/
22
+ - https://www.globalbrandsmagazine.com/beverage-brands-in-the-middle-east/
23
+ - https://www.greenqueen.com.hk/
24
+ - https://www.healthcaremea.com/
25
+ - https://www.ingredientsnetwork.com/
26
+ - https://www.iresearch.cn/
27
+ - https://issuu.com/ruralnewsgroup/docs/dn_508_november_22
28
+ - https://www.just-food.com/
29
+ - https://www.kantarworldpanel.com/cn
30
+ - https://www.labelsandlabeling.com/
31
+ - https://lenta.ru
32
+ - https://www.livemint.com/
33
+ - https://www.magzter.com/ar/HK/Ringier-Trade-Media-Ltd/Food-Manufacturing-Journal---Middle-East-&-Africa/Food-&-Beverage/194798
34
+ - https://www.mckinsey.com.cn/
35
+ - https://milknews.ru/
36
+ - https://www.nutritioninsight.com/
37
+ - https://www.packagingnetwork.com/doc/fruit-juice-maker-chooses-drink-box-for-grown-0001
38
+ - https://www.packagingnews.co.uk/
39
+ - https://packagingsouthasia.com/
40
+ - https://www.packaginglaw.com/
41
+ - https://www.pwccn.com/zh/research-and-insights.html
42
+ - https://www.retail.ru/
43
+ - https://retailer.ru/
44
+ - https://www.rbc.ru/
45
+ - https://rg.ru/
46
+ - https://sustainabilitymea.com/
47
+ - https://www.foodsafetyafrica.net/fda-announces-elimination-of-pfas-in-food-packaging-to-protect-public-health/
48
+ - https://tass.ru
49
+ - https://thebeet.com/category/plant-based-news/
50
+ - https://www.thegrocer.co.uk/
51
+ - https://www.theveganindians.com/
52
+ - https://www.thevegankind.com/
53
+ - https://www.totallyveganbuzz.com/
54
+ - https://www.unipack.ru/
55
+ - https://vegnews.com/
56
+ - https://www.veganfirst.com/
57
+ - https://www.theveganindians.com/indian-start-up-launches-first-ever-vegan-milk-made-from-sprouted-millets/
58
+ - https://vegoutmag.com/food-and-drink
59
+ - https://www.ecfr.gov/
60
+ - https://freepub.edqm.eu/publications/
61
+ - https://www.bfr.bund.de/de/bfr_empfehlungen_fuer_materialien_im_lebensmittelkontakt-308425.html
62
+ - https://cfsa.net.cn/
63
+ - https://www.icourse163.org/
64
+ - https://www.doc88.com/
65
+ - https://www.foodbev.com/news/category/industries/beverage/
66
+ - https://www.packagingdigest.com/food-beverage/beverage-packaging
67
+ - https://www.packagingnews.co.uk/news/materials/cartonboard
68
+ - https://www.dairyreporter.com/
69
+ - https://www.packaginginsights.com
70
+ - https://packagingeurope.com/
71
+ - https://www.foodnavigator-asia.com/
72
+ - https://www.newsnow.co.uk/h/Industry+Sectors/Food+&+Drink/Food+Manufacturers
73
+ - https://www.beveragedaily.com/
74
+ - https://www.fb101.com/
75
+ - https://www.dairyfoods.com/
76
+ - https://www.foodengineeringmag.com/
77
+ - https://www.preparedfoods.com/
78
+ - https://www.bevindustry.com/
79
+ - https://www.foodnavigator-usa.com/
80
+ - https://www.foodnavigator-latam.com/
81
+ - https://www.foodnavigator.com/
82
+ - https://www.apfoodonline.com/
83
+ - https://www.food-safety.com/
84
+ - https://www.foodbusinessgulf.com/
85
+ - https://www.bevnet.com/
86
+ - https://imbibemagazine.com/
87
+ - https://beveragedynamics.com/
88
+ - https://www.beverage-digest.com/
89
+ - https://www.thebeveragejournal.com/
90
+ - https://foodchainmagazine.com/news/category/insights/
91
+ - https://www.just-drinks.com/
92
+ - https://www.newfoodmagazine.com/
93
+ - https://beverage-master.com/
94
+ - https://drinksint.com/
95
+ - https://dairynews.today/global/
96
+ - https://globaldairyplatform.com/news_category/dairy-media-news/
97
+ - https://www.usdairy.com/media
98
+ - https://dairybusiness.com/
99
+ - https://www.thedairysite.com/
100
+ - https://californiadairymagazine.com/
101
+ - https://www.dairynewsaustralia.com.au/
102
+ - https://www.dairynz.co.nz/news/
103
+ - https://international-dairy.com/media-information/
104
+ - https://www.packaging-gateway.com/news/
105
+ - https://www.packworld.com/
106
+ - https://foodbeverageasia.com/
107
+ - https://www.nspackaging.com/
108
+ - https://www.packagingstrategies.com/
109
+ - https://spnews.com/
110
+ - https://www.foodbusinessafrica.com/
111
+ - https://www.foodmanufacture.co.uk/#
112
+ - https://www.weibo.com/u/2096538335?lpage=profileRecom
113
+ - http://www.csztv.cn/
114
+ - https://www.cbndata.com/
115
+ - https://news.qq.com/
116
+ - http://www.foodmate.net/
117
+ - https://www.foodingredientsfirst.com/
118
+ - https://www.beveragemarketing.com/strategist.asp
119
+ - https://www.ift.org/news-and-publications/food-technology-magazine
120
+ - https://www.packagingstrategies.com/flexible-packaging
121
+ - https://www.compacknews.news/en/
122
+ - https://www.italiaimballaggio.it/
123
+ - https://www.italiagrafica.com/
124
+ - https://www.portalspozywczy.pl/
125
+ - https://www.clal.it/
126
+ - https://www.pack.com.br/
127
+ - https://www.plastico.com.br/category/embalagens/
128
+ - https://www.canaldoleite.com/
129
+ - https://www.foodtalks.cn/
130
+ - https://www.foodaily.com/
131
+ - https://www.iimedia.cn/
132
+ - https://www.cdia.org.cn/
133
+ - https://www.chinacoatingnet.com/
134
+ - http://www.cppia.org.cn/
135
+ - https://www.dac.org.cn/